Skip to content

Commit 2f99497

Browse files
authored
Expose secure/non-secure URIs on service instance, optimize Eureka (#1604)
* Expose secure/non-secure uris on service instance * Optimize returned instances from Eureka
1 parent 176e7da commit 2f99497

20 files changed

+235
-185
lines changed

src/Common/src/Common/Discovery/IServiceInstance.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public interface IServiceInstance
3636
/// </summary>
3737
Uri Uri { get; }
3838

39+
/// <summary>
40+
/// Gets the HTTP-based resolved address of the registered service instance, if available.
41+
/// </summary>
42+
Uri? NonSecureUri { get; }
43+
44+
/// <summary>
45+
/// Gets the HTTPS-based resolved address of the registered service instance, if available.
46+
/// </summary>
47+
Uri? SecureUri { get; }
48+
3949
/// <summary>
4050
/// Gets the key/value metadata associated with this service instance.
4151
/// </summary>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
#nullable enable
22
Steeltoe.Common.Discovery.IServiceInstance.InstanceId.get -> string!
3+
Steeltoe.Common.Discovery.IServiceInstance.NonSecureUri.get -> System.Uri?
4+
Steeltoe.Common.Discovery.IServiceInstance.SecureUri.get -> System.Uri?

src/Configuration/test/ConfigServer.Test/ConfigServerConfigurationProviderTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,8 @@ private sealed class TestServiceInstance(string serviceId, string instanceId, Ur
424424
public int Port { get; } = uri.Port;
425425
public bool IsSecure { get; } = uri.Scheme == Uri.UriSchemeHttps;
426426
public Uri Uri { get; } = uri;
427+
public Uri? NonSecureUri => IsSecure ? null : Uri;
428+
public Uri? SecureUri => IsSecure ? Uri : null;
427429
public IReadOnlyDictionary<string, string?> Metadata { get; } = metadata;
428430
}
429431
}

src/Discovery/src/Configuration/ConfigurationServiceInstance.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ public sealed class ConfigurationServiceInstance : IServiceInstance
3838
/// <inheritdoc />
3939
public Uri Uri => new($"{(IsSecure ? Uri.UriSchemeHttps : Uri.UriSchemeHttp)}{Uri.SchemeDelimiter}{Host}:{Port}");
4040

41+
/// <inheritdoc />
42+
public Uri? NonSecureUri => IsSecure ? null : Uri;
43+
44+
/// <inheritdoc />
45+
public Uri? SecureUri => IsSecure ? Uri : null;
46+
4147
/// <inheritdoc cref="IServiceInstance.Metadata" />
4248
public IDictionary<string, string?> Metadata { get; } = new Dictionary<string, string?>();
4349
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
#nullable enable
22
Steeltoe.Discovery.Configuration.ConfigurationServiceInstance.InstanceId.get -> string!
3+
Steeltoe.Discovery.Configuration.ConfigurationServiceInstance.NonSecureUri.get -> System.Uri?
4+
Steeltoe.Discovery.Configuration.ConfigurationServiceInstance.SecureUri.get -> System.Uri?

src/Discovery/src/Consul/ConsulServiceInstance.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ internal sealed class ConsulServiceInstance : IServiceInstance
3131
/// <inheritdoc />
3232
public Uri Uri { get; }
3333

34+
/// <inheritdoc />
35+
public Uri? NonSecureUri { get; }
36+
37+
/// <inheritdoc />
38+
public Uri? SecureUri { get; }
39+
3440
public IReadOnlyList<string> Tags { get; }
3541

3642
/// <inheritdoc />
@@ -54,5 +60,7 @@ internal ConsulServiceInstance(ServiceEntry serviceEntry)
5460
InstanceId = serviceEntry.Service.ID;
5561
Port = serviceEntry.Service.Port;
5662
Uri = new Uri($"{(IsSecure ? "https" : "http")}://{Host}:{Port}");
63+
NonSecureUri = IsSecure ? null : Uri;
64+
SecureUri = IsSecure ? Uri : null;
5765
}
5866
}

src/Discovery/src/Consul/Registry/ConsulRegistration.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ internal sealed class ConsulRegistration : IServiceInstance
3737
/// <inheritdoc />
3838
public Uri Uri => new($"{_optionsMonitor.CurrentValue.EffectiveScheme}://{Host}:{Port}");
3939

40+
/// <inheritdoc />
41+
public Uri? NonSecureUri => IsSecure ? null : Uri;
42+
43+
/// <inheritdoc />
44+
public Uri? SecureUri => IsSecure ? Uri : null;
45+
4046
public IReadOnlyList<string> Tags { get; }
4147

4248
/// <inheritdoc />

src/Discovery/src/Consul/ThisServiceInstance.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ internal sealed class ThisServiceInstance : IServiceInstance
3030
/// <inheritdoc />
3131
public Uri Uri { get; }
3232

33+
/// <inheritdoc />
34+
public Uri? NonSecureUri { get; }
35+
36+
/// <inheritdoc />
37+
public Uri? SecureUri { get; }
38+
3339
/// <inheritdoc />
3440
public IReadOnlyDictionary<string, string?> Metadata { get; }
3541

@@ -44,5 +50,7 @@ public ThisServiceInstance(ConsulRegistration registration)
4450
Port = registration.Port;
4551
Metadata = registration.Metadata;
4652
Uri = registration.Uri;
53+
NonSecureUri = registration.NonSecureUri;
54+
SecureUri = registration.SecureUri;
4755
}
4856
}

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)