Skip to content

Commit c4ce8a3

Browse files
committed
Optimize returned instances from Eureka
1 parent 8dd7597 commit c4ce8a3

File tree

4 files changed

+126
-171
lines changed

4 files changed

+126
-171
lines changed

src/Discovery/src/Eureka/AppInfo/ApplicationInfoCollection.cs

Lines changed: 39 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Collections;
66
using System.Collections.Concurrent;
7+
using System.Collections.ObjectModel;
78
using System.Text;
89
using System.Text.Json;
910
using Steeltoe.Common;
@@ -17,11 +18,8 @@ namespace Steeltoe.Discovery.Eureka.AppInfo;
1718
/// </summary>
1819
public sealed class ApplicationInfoCollection : IReadOnlyCollection<ApplicationInfo>
1920
{
20-
private readonly object _addRemoveInstanceLock = new();
21-
2221
internal ConcurrentDictionary<string, ApplicationInfo> ApplicationMap { get; } = new();
2322
internal ConcurrentDictionary<string, ConcurrentDictionary<string, InstanceInfo>> VipInstanceMap { get; } = new();
24-
internal ConcurrentDictionary<string, ConcurrentDictionary<string, InstanceInfo>> SecureVipInstanceMap { get; } = new();
2523

2624
public string? AppsHashCode { get; internal set; }
2725
public long? Version { get; private set; }
@@ -51,18 +49,25 @@ internal ApplicationInfoCollection(IList<ApplicationInfo> apps)
5149
return ApplicationMap.GetValueOrDefault(appName.ToUpperInvariant());
5250
}
5351

54-
internal List<InstanceInfo> GetInstancesBySecureVipAddress(string secureVipAddress)
52+
internal ReadOnlyCollection<InstanceInfo> GetInstancesByVipAddress(string vipAddress)
5553
{
56-
ArgumentException.ThrowIfNullOrWhiteSpace(secureVipAddress);
54+
ArgumentException.ThrowIfNullOrWhiteSpace(vipAddress);
5755

58-
return GetByVipAddress(secureVipAddress, SecureVipInstanceMap);
59-
}
56+
List<InstanceInfo> result = [];
57+
string addressUpper = vipAddress.ToUpperInvariant();
6058

61-
internal List<InstanceInfo> GetInstancesByVipAddress(string vipAddress)
62-
{
63-
ArgumentException.ThrowIfNullOrWhiteSpace(vipAddress);
59+
if (VipInstanceMap.TryGetValue(addressUpper, out ConcurrentDictionary<string, InstanceInfo>? instancesById))
60+
{
61+
foreach (InstanceInfo instance in instancesById.Values)
62+
{
63+
if ((ReturnUpInstancesOnly && instance.EffectiveStatus == InstanceStatus.Up) || !ReturnUpInstancesOnly)
64+
{
65+
result.Add(instance);
66+
}
67+
}
68+
}
6469

65-
return GetByVipAddress(vipAddress, VipInstanceMap);
70+
return result.AsReadOnly();
6671
}
6772

6873
/// <inheritdoc />
@@ -92,79 +97,51 @@ internal void Add(ApplicationInfo app)
9297

9398
foreach (InstanceInfo instance in app.Instances)
9499
{
95-
AddInstanceToVip(instance);
100+
AddToVipInstanceMap(instance);
96101
}
97102
}
98103

99-
private void AddInstanceToVip(InstanceInfo instance)
104+
private void AddToVipInstanceMap(InstanceInfo instance)
100105
{
101-
foreach (string vipAddress in ExpandVipAddresses(instance.VipAddress))
106+
foreach (string vipAddress in ExpandVipAddresses(instance))
102107
{
103-
AddInstanceToVip(instance, vipAddress, VipInstanceMap);
104-
}
108+
string addressUpper = vipAddress.ToUpperInvariant();
105109

106-
foreach (string secureVipAddress in ExpandVipAddresses(instance.SecureVipAddress))
107-
{
108-
AddInstanceToVip(instance, secureVipAddress, SecureVipInstanceMap);
110+
ConcurrentDictionary<string, InstanceInfo> instancesById = VipInstanceMap.GetOrAdd(addressUpper, new ConcurrentDictionary<string, InstanceInfo>());
111+
instancesById.AddOrUpdate(instance.InstanceId, _ => instance, (_, _) => instance);
109112
}
110113
}
111114

112-
private void AddInstanceToVip(InstanceInfo instance, string address, ConcurrentDictionary<string, ConcurrentDictionary<string, InstanceInfo>> dictionary)
115+
private static HashSet<string> ExpandVipAddresses(InstanceInfo instance)
113116
{
114-
lock (_addRemoveInstanceLock)
115-
{
116-
string addressUpper = address.ToUpperInvariant();
117-
118-
if (!dictionary.TryGetValue(addressUpper, out ConcurrentDictionary<string, InstanceInfo>? instances))
119-
{
120-
instances = new ConcurrentDictionary<string, InstanceInfo>();
121-
dictionary[addressUpper] = instances;
122-
}
117+
HashSet<string> addresses = [];
123118

124-
instances[instance.InstanceId] = instance;
119+
if (instance.SecureVipAddress != null)
120+
{
121+
string[] secureAddresses = instance.SecureVipAddress.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
122+
addresses.UnionWith(secureAddresses);
125123
}
126-
}
127124

128-
private static string[] ExpandVipAddresses(string? addresses)
129-
{
130-
if (string.IsNullOrWhiteSpace(addresses))
125+
if (instance.VipAddress != null)
131126
{
132-
return [];
127+
string[] vipAddresses = instance.VipAddress.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
128+
addresses.UnionWith(vipAddresses);
133129
}
134130

135-
return addresses.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
131+
return addresses;
136132
}
137133

138-
internal void RemoveInstanceFromVip(InstanceInfo instance)
134+
internal void RemoveFromVipInstanceMap(InstanceInfo instance)
139135
{
140136
ArgumentNullException.ThrowIfNull(instance);
141137

142-
foreach (string vipAddress in ExpandVipAddresses(instance.VipAddress))
143-
{
144-
RemoveInstanceFromVip(instance, vipAddress, VipInstanceMap);
145-
}
146-
147-
foreach (string secureVipAddress in ExpandVipAddresses(instance.SecureVipAddress))
148-
{
149-
RemoveInstanceFromVip(instance, secureVipAddress, SecureVipInstanceMap);
150-
}
151-
}
152-
153-
private void RemoveInstanceFromVip(InstanceInfo instance, string address,
154-
ConcurrentDictionary<string, ConcurrentDictionary<string, InstanceInfo>> dictionary)
155-
{
156-
lock (_addRemoveInstanceLock)
138+
foreach (string vipAddress in ExpandVipAddresses(instance))
157139
{
158-
string addressUpper = address.ToUpperInvariant();
140+
string addressUpper = vipAddress.ToUpperInvariant();
159141

160-
if (dictionary.TryGetValue(addressUpper, out ConcurrentDictionary<string, InstanceInfo>? instances))
142+
if (VipInstanceMap.TryGetValue(addressUpper, out ConcurrentDictionary<string, InstanceInfo>? instancesById))
161143
{
162-
_ = instances.TryRemove(instance.InstanceId, out _);
163-
164-
if (instances.IsEmpty)
165-
{
166-
_ = dictionary.TryRemove(addressUpper, out _);
167-
}
144+
instancesById.TryRemove(instance.InstanceId, out _);
168145
}
169146
}
170147
}
@@ -190,11 +167,11 @@ internal void UpdateFromDelta(ApplicationInfoCollection delta)
190167
case ActionType.Added:
191168
case ActionType.Modified:
192169
existingApp.Add(instance);
193-
AddInstanceToVip(instance);
170+
AddToVipInstanceMap(instance);
194171
break;
195172
case ActionType.Deleted:
196173
existingApp.Remove(instance);
197-
RemoveInstanceFromVip(instance);
174+
RemoveFromVipInstanceMap(instance);
198175
break;
199176
}
200177
}
@@ -261,22 +238,4 @@ internal static ApplicationInfoCollection FromJson(JsonApplications? jsonApplica
261238

262239
return apps;
263240
}
264-
265-
private List<InstanceInfo> GetByVipAddress(string name, ConcurrentDictionary<string, ConcurrentDictionary<string, InstanceInfo>> dictionary)
266-
{
267-
List<InstanceInfo> result = [];
268-
269-
if (dictionary.TryGetValue(name.ToUpperInvariant(), out ConcurrentDictionary<string, InstanceInfo>? instances))
270-
{
271-
foreach (InstanceInfo instance in instances.Values.ToArray())
272-
{
273-
if ((ReturnUpInstancesOnly && instance.EffectiveStatus == InstanceStatus.Up) || !ReturnUpInstancesOnly)
274-
{
275-
result.Add(instance);
276-
}
277-
}
278-
}
279-
280-
return result;
281-
}
282241
}

src/Discovery/src/Eureka/EurekaDiscoveryClient.cs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Text;
56
using Microsoft.Extensions.Logging;
67
using Microsoft.Extensions.Options;
78
using Steeltoe.Common.Discovery;
@@ -156,12 +157,11 @@ public EurekaDiscoveryClient(EurekaApplicationInfoManager appInfoManager, Eureka
156157
return Applications.GetRegisteredApplication(appName);
157158
}
158159

159-
internal IReadOnlyList<InstanceInfo> GetInstancesByVipAddress(string vipAddress, bool secure)
160+
internal IReadOnlyList<InstanceInfo> GetInstancesByVipAddress(string vipAddress)
160161
{
161162
ArgumentException.ThrowIfNullOrWhiteSpace(vipAddress);
162163

163-
List<InstanceInfo> instances = secure ? Applications.GetInstancesBySecureVipAddress(vipAddress) : Applications.GetInstancesByVipAddress(vipAddress);
164-
return instances.AsReadOnly();
164+
return Applications.GetInstancesByVipAddress(vipAddress);
165165
}
166166

167167
/// <inheritdoc />
@@ -533,18 +533,40 @@ public Task<IList<IServiceInstance>> GetInstancesAsync(string serviceId, Cancell
533533
{
534534
ArgumentException.ThrowIfNullOrWhiteSpace(serviceId);
535535

536-
IReadOnlyList<InstanceInfo> nonSecureInstances = GetInstancesByVipAddress(serviceId, false);
537-
IReadOnlyList<InstanceInfo> secureInstances = GetInstancesByVipAddress(serviceId, true);
538-
539-
IEnumerable<InstanceInfo> instances = secureInstances.Concat(nonSecureInstances).DistinctBy(instance => instance.InstanceId);
536+
IReadOnlyList<InstanceInfo> instances = GetInstancesByVipAddress(serviceId);
540537
IServiceInstance[] serviceInstances = instances.Select(instance => new EurekaServiceInstance(instance)).Cast<IServiceInstance>().ToArray();
541538

542-
_logger.LogDebug("Returning {Count} service instances: {ServiceInstances}", serviceInstances.Length,
543-
string.Join(", ", serviceInstances.Select(instance => $"{instance.ServiceId}={instance.Uri}")));
539+
if (_logger.IsEnabled(LogLevel.Debug))
540+
{
541+
string instanceNames = string.Join(", ", serviceInstances.Select(FormatServiceInstance));
542+
_logger.LogDebug("Returning {Count} service instances for '{ServiceId}': {ServiceInstances}", serviceInstances.Length, serviceId, instanceNames);
543+
}
544544

545545
return Task.FromResult<IList<IServiceInstance>>(serviceInstances);
546546
}
547547

548+
private static string FormatServiceInstance(IServiceInstance instance)
549+
{
550+
var builder = new StringBuilder();
551+
552+
if (instance.SecureUri != null)
553+
{
554+
builder.Append(instance.SecureUri);
555+
}
556+
557+
if (instance.NonSecureUri != null)
558+
{
559+
if (builder.Length > 0)
560+
{
561+
builder.Append(';');
562+
}
563+
564+
builder.Append(instance.NonSecureUri);
565+
}
566+
567+
return $"{instance.InstanceId}={builder}";
568+
}
569+
548570
/// <inheritdoc />
549571
public IServiceInstance GetLocalServiceInstance()
550572
{

0 commit comments

Comments
 (0)