🔨 Reconfigured to use new discovery
This commit is contained in:
@@ -9,6 +9,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "develop"; });
|
||||||
|
|
||||||
builder.ConfigureAppKestrel(builder.Configuration);
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|
||||||
builder.Services.AddAppServices(builder.Configuration);
|
builder.Services.AddAppServices(builder.Configuration);
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_develop;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_develop;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"KnownProxies": [
|
"KnownProxies": [
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "drive"; });
|
||||||
|
|
||||||
// Configure Kestrel and server options
|
// Configure Kestrel and server options
|
||||||
builder.ConfigureAppKestrel(builder.Configuration, maxRequestBodySize: long.MaxValue);
|
builder.ConfigureAppKestrel(builder.Configuration, maxRequestBodySize: long.MaxValue);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_drive;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"Authentication": {
|
"Authentication": {
|
||||||
"Schemes": {
|
"Schemes": {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "insight"; });
|
||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
builder.ConfigureAppKestrel(builder.Configuration);
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_insight;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_insight;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"KnownProxies": [
|
"KnownProxies": [
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "pass"; });
|
||||||
|
|
||||||
// Configure Kestrel and server options
|
// Configure Kestrel and server options
|
||||||
builder.ConfigureAppKestrel(builder.Configuration);
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_pass;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"Authentication": {
|
"Authentication": {
|
||||||
"Schemes": {
|
"Schemes": {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "ring"; });
|
||||||
|
|
||||||
// Configure Kestrel and server options
|
// Configure Kestrel and server options
|
||||||
builder.ConfigureAppKestrel(builder.Configuration);
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_ring;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_ring;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"Notifications": {
|
"Notifications": {
|
||||||
"Push": {
|
"Push": {
|
||||||
|
|||||||
31
DysonNetwork.Shared/Configurator/ConfigureService.cs
Normal file
31
DysonNetwork.Shared/Configurator/ConfigureService.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Shared.Configurator;
|
||||||
|
|
||||||
|
public class ConfigureService(ServiceRegistrar registrar)
|
||||||
|
{
|
||||||
|
private readonly ServiceRegistrar _registrar = registrar;
|
||||||
|
|
||||||
|
public async Task<JsonDocument> GetConfigurationsAsync()
|
||||||
|
{
|
||||||
|
var instance = await _registrar.GetServiceInstanceAsync("config", "http");
|
||||||
|
using var client = new HttpClient();
|
||||||
|
var response = await client.GetStringAsync($"http://{instance}/config");
|
||||||
|
var json = JsonDocument.Parse(response);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ConfigureAppAsync(IConfigurationBuilder builder)
|
||||||
|
{
|
||||||
|
var configs = await GetConfigurationsAsync();
|
||||||
|
if (configs.RootElement.TryGetProperty("connection_strings", out var csElement))
|
||||||
|
{
|
||||||
|
var csJson = csElement.ToString();
|
||||||
|
var stream = new MemoryStream(Encoding.UTF8.GetBytes(csJson));
|
||||||
|
builder.AddJsonStream(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ public static class KestrelConfiguration
|
|||||||
if (enableGrpc)
|
if (enableGrpc)
|
||||||
{
|
{
|
||||||
// gRPC
|
// gRPC
|
||||||
var grpcPort = int.Parse(configuration.GetValue("GRPC_PORT", "5001"));
|
var grpcPort = int.Parse(configuration.GetValue("GRPC_PORT", "5000"));
|
||||||
options.ListenAnyIP(grpcPort, listenOptions =>
|
options.ListenAnyIP(grpcPort, listenOptions =>
|
||||||
{
|
{
|
||||||
listenOptions.Protocols = HttpProtocols.Http2;
|
listenOptions.Protocols = HttpProtocols.Http2;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Etcdserverpb;
|
|||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace DysonNetwork.Shared.Registry;
|
namespace DysonNetwork.Shared.Registry;
|
||||||
|
|
||||||
@@ -12,14 +13,21 @@ public class ServiceRegistrar(EtcdClient etcd)
|
|||||||
private string? _serviceKey;
|
private string? _serviceKey;
|
||||||
private long _leaseId;
|
private long _leaseId;
|
||||||
private long _ttlSeconds;
|
private long _ttlSeconds;
|
||||||
|
private readonly Dictionary<string, int> _roundRobinCounters = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the service in etcd with a TTL lease.
|
/// Register the service in etcd with a TTL lease.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task RegisterAsync(string serviceName, string servicePart, string instanceId, string host, int port, long ttlSeconds = 30)
|
public async Task RegisterAsync(
|
||||||
|
string serviceName,
|
||||||
|
string servicePart,
|
||||||
|
string instanceId,
|
||||||
|
string host,
|
||||||
|
int port,
|
||||||
|
long ttlSeconds = 30)
|
||||||
{
|
{
|
||||||
_ttlSeconds = ttlSeconds;
|
_ttlSeconds = ttlSeconds;
|
||||||
_serviceKey = $"/services/{serviceName}/${servicePart}/{instanceId}";
|
_serviceKey = $"/services/{serviceName}/{servicePart}/{instanceId}";
|
||||||
var serviceValue = $"{host}:{port}";
|
var serviceValue = $"{host}:{port}";
|
||||||
|
|
||||||
// Create and store TTL lease
|
// Create and store TTL lease
|
||||||
@@ -70,20 +78,66 @@ public class ServiceRegistrar(EtcdClient etcd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all service instances for a specific service name and part.
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<string>> GetServiceInstancesAsync(string serviceName, string servicePart)
|
||||||
|
{
|
||||||
|
var prefix = $"/services/{serviceName}/{servicePart}/";
|
||||||
|
var request = new RangeRequest
|
||||||
|
{
|
||||||
|
Key = ByteString.CopyFromUtf8(prefix),
|
||||||
|
RangeEnd = ByteString.CopyFromUtf8(prefix + "\0")
|
||||||
|
};
|
||||||
|
var response = await etcd.GetAsync(request);
|
||||||
|
var instances = response.Kvs.Select(kv => kv.Value.ToStringUtf8()).ToList();
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a single service instance with load balancing (round-robin).
|
||||||
|
/// </summary>
|
||||||
|
public async Task<string> GetServiceInstanceAsync(string serviceName, string servicePart)
|
||||||
|
{
|
||||||
|
var instances = await GetServiceInstancesAsync(serviceName, servicePart);
|
||||||
|
if (instances.Count == 0)
|
||||||
|
throw new InvalidOperationException($"No instances found for service '{serviceName}' part '{servicePart}'");
|
||||||
|
var key = $"{serviceName}/{servicePart}";
|
||||||
|
if (!_roundRobinCounters.ContainsKey(key))
|
||||||
|
_roundRobinCounters[key] = 0;
|
||||||
|
var instance = instances[_roundRobinCounters[key] % instances.Count];
|
||||||
|
_roundRobinCounters[key] = (_roundRobinCounters[key] + 1) % int.MaxValue;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ServiceRegistrarHostedService(ServiceRegistrar registrar, IConfiguration config) : IHostedService
|
public sealed class ServiceRegistrationOptions
|
||||||
{
|
{
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
public string Host { get; set; } = "127.0.0.1";
|
||||||
|
public string InstanceId { get; set; } = Guid.NewGuid().ToString("N");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServiceRegistrarHostedService(
|
||||||
|
ServiceRegistrar registrar,
|
||||||
|
IConfiguration configuration,
|
||||||
|
IOptions<ServiceRegistrationOptions> options
|
||||||
|
)
|
||||||
|
: IHostedService
|
||||||
|
{
|
||||||
|
private readonly ServiceRegistrationOptions _opts = options.Value;
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var name = config["Service:Name"];
|
var grpcPort = int.Parse(configuration.GetValue("GRPC_PORT", "5000"));
|
||||||
var host = config["Service:Host"];
|
await registrar.RegisterAsync(_opts.Name, "grpc", _opts.InstanceId, _opts.Host, grpcPort);
|
||||||
var grpcPort = int.Parse(config["Service:GrpcPort"]!);
|
|
||||||
var httpPort = int.Parse(config["Service:HttpPort"]!);
|
|
||||||
var instanceId = config["Service:InstanceId"] ?? Guid.NewGuid().ToString("N");
|
|
||||||
|
|
||||||
await registrar.RegisterAsync(name, "grpc", instanceId, host, grpcPort);
|
var httpPorts = configuration.GetValue("HTTP_PORTS", "6000")
|
||||||
await registrar.RegisterAsync(name, "http", instanceId, host, httpPort);
|
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(p => int.Parse(p.Trim()))
|
||||||
|
.ToArray();
|
||||||
|
await registrar.RegisterAsync(_opts.Name, "http", _opts.InstanceId, _opts.Host, httpPorts.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync(CancellationToken cancellationToken)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "sphere"; });
|
||||||
|
|
||||||
// Configure Kestrel and server options
|
// Configure Kestrel and server options
|
||||||
builder.ConfigureAppKestrel(builder.Configuration);
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_sphere;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_sphere;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"GeoIp": {
|
"GeoIp": {
|
||||||
"DatabasePath": "./Keys/GeoLite2-City.mmdb"
|
"DatabasePath": "./Keys/GeoLite2-City.mmdb"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
|
builder.Services.Configure<ServiceRegistrationOptions>(opts => { opts.Name = "zone"; });
|
||||||
|
|
||||||
builder.ConfigureAppKestrel(builder.Configuration);
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
|
|||||||
@@ -8,7 +8,10 @@
|
|||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"App": "Host=localhost;Port=5432;Database=dyson_zone;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60"
|
"App": "Host=localhost;Port=5432;Database=dyson_zone;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"Registrar": "127.0.0.1:2379",
|
||||||
|
"Cache": "127.0.0.1:6379",
|
||||||
|
"Queue": "127.0.0.1:4222"
|
||||||
},
|
},
|
||||||
"KnownProxies": [
|
"KnownProxies": [
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user