♻️ Remove etcd, replace with asprie. Move infra to aspire. Disable gateway for now
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Yarp.ReverseProxy.Forwarder;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public static class GatewayReverseProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides reverse proxy for DysonNetwork.Gateway.
|
||||
/// Give the ability to the contained frontend to access other services via the gateway.
|
||||
/// </summary>
|
||||
/// <param name="app">The asp.net core application</param>
|
||||
/// <returns>The modified application</returns>
|
||||
public static WebApplication MapGatewayProxy(this WebApplication app)
|
||||
{
|
||||
var httpClient = new HttpMessageInvoker(new SocketsHttpHandler
|
||||
{
|
||||
UseProxy = false,
|
||||
AllowAutoRedirect = true,
|
||||
AutomaticDecompression = DecompressionMethods.All,
|
||||
UseCookies = true,
|
||||
EnableMultipleHttp2Connections = true,
|
||||
ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
|
||||
ConnectTimeout = TimeSpan.FromSeconds(15),
|
||||
});
|
||||
|
||||
var transformer = new GatewayReverseProxyTransformer();
|
||||
var requestConfig = new ForwarderRequestConfig();
|
||||
|
||||
app.Map("/cgi/{**catch-all}", async (HttpContext context, IHttpForwarder forwarder) =>
|
||||
{
|
||||
var registry = context.RequestServices.GetRequiredService<ServiceRegistry>();
|
||||
var gatewayUrl = await registry.GetServiceUrl("DysonNetwork.Gateway");
|
||||
if (gatewayUrl is null)
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
await context.Response.WriteAsync("Gateway not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var error = await forwarder.SendAsync(
|
||||
context,
|
||||
gatewayUrl,
|
||||
httpClient,
|
||||
requestConfig,
|
||||
transformer
|
||||
);
|
||||
if (error != ForwarderError.None)
|
||||
{
|
||||
var errorFeature = context.GetForwarderErrorFeature();
|
||||
var exception = errorFeature?.Exception;
|
||||
context.Response.StatusCode = 502;
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync($"Gateway remote error: {exception?.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
public class GatewayReverseProxyTransformer : HttpTransformer
|
||||
{
|
||||
private const string Value = "/cgi";
|
||||
|
||||
public override ValueTask TransformRequestAsync(
|
||||
HttpContext httpContext,
|
||||
HttpRequestMessage proxyRequest,
|
||||
string destinationPrefix,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
httpContext.Request.Path = httpContext.Request.Path.StartsWithSegments(Value, out var remaining)
|
||||
? remaining
|
||||
: httpContext.Request.Path;
|
||||
|
||||
return Default.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public class RegistryHostedService(
|
||||
ServiceRegistry serviceRegistry,
|
||||
IConfiguration configuration,
|
||||
ILogger<RegistryHostedService> logger
|
||||
)
|
||||
: IHostedService
|
||||
{
|
||||
private CancellationTokenSource? _cts;
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var serviceName = configuration["Service:Name"];
|
||||
var serviceUrl = configuration["Service:Url"];
|
||||
var insecure = configuration.GetValue<bool>("Etcd:Insecure");
|
||||
var remote = configuration.GetConnectionString("Etcd");
|
||||
|
||||
if (insecure)
|
||||
logger.LogWarning("Etcd is configured to use insecure channel.");
|
||||
|
||||
if (string.IsNullOrEmpty(serviceUrl) || string.IsNullOrEmpty(serviceName))
|
||||
{
|
||||
logger.LogWarning("Service URL or Service Name was not configured. Skipping Etcd registration.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
"Registering service {ServiceName} at {ServiceUrl} with Etcd ({Remote}).",
|
||||
serviceName,
|
||||
serviceUrl,
|
||||
remote
|
||||
);
|
||||
try
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
await serviceRegistry.RegisterService(serviceName, serviceUrl, cancellationToken: _cts.Token);
|
||||
logger.LogInformation("Service {ServiceName} registered successfully.", serviceName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to register service {ServiceName} with Etcd.", serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
|
||||
// The lease will expire automatically if the service stops ungracefully.
|
||||
var serviceName = configuration["Service:Name"];
|
||||
if (serviceName is not null)
|
||||
await serviceRegistry.UnregisterService(serviceName);
|
||||
logger.LogInformation("Service registration hosted service is stopping.");
|
||||
}
|
||||
}
|
@@ -1,4 +1,3 @@
|
||||
using dotnet_etcd.interfaces;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -9,80 +8,35 @@ public static class ServiceInjectionHelper
|
||||
{
|
||||
public static IServiceCollection AddRingService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<RingService.RingServiceClient>(sp =>
|
||||
services.AddGrpcClient<RingService.RingServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateRingServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
});
|
||||
o.Address = new Uri("https://ring");
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAccountService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<AccountService.AccountServiceClient>(sp =>
|
||||
services.AddGrpcClient<AccountService.AccountServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateAccountServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://pass");
|
||||
});
|
||||
services.AddSingleton<AccountClientHelper>();
|
||||
|
||||
services.AddSingleton<BotAccountReceiverService.BotAccountReceiverServiceClient>(sp =>
|
||||
services.AddGrpcClient<BotAccountReceiverService.BotAccountReceiverServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateBotAccountReceiverServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://pass");
|
||||
});
|
||||
|
||||
services.AddSingleton<ActionLogService.ActionLogServiceClient>(sp =>
|
||||
services.AddGrpcClient<ActionLogService.ActionLogServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateActionLogServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://pass");
|
||||
});
|
||||
|
||||
services.AddSingleton<PaymentService.PaymentServiceClient>(sp =>
|
||||
services.AddGrpcClient<PaymentService.PaymentServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreatePaymentServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://pass");
|
||||
});
|
||||
|
||||
return services;
|
||||
@@ -90,32 +44,14 @@ public static class ServiceInjectionHelper
|
||||
|
||||
public static IServiceCollection AddDriveService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<FileService.FileServiceClient>(sp =>
|
||||
services.AddGrpcClient<FileService.FileServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateFileServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://drive");
|
||||
});
|
||||
|
||||
services.AddSingleton<FileReferenceService.FileReferenceServiceClient>(sp =>
|
||||
services.AddGrpcClient<FileReferenceService.FileReferenceServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateFileReferenceServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://drive");
|
||||
});
|
||||
|
||||
return services;
|
||||
@@ -123,18 +59,9 @@ public static class ServiceInjectionHelper
|
||||
|
||||
public static IServiceCollection AddPublisherService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<PublisherService.PublisherServiceClient>(sp =>
|
||||
services.AddGrpcClient<PublisherService.PublisherServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreatePublisherServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://sphere");
|
||||
});
|
||||
|
||||
return services;
|
||||
@@ -142,18 +69,9 @@ public static class ServiceInjectionHelper
|
||||
|
||||
public static IServiceCollection AddDevelopService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<CustomAppService.CustomAppServiceClient>(sp =>
|
||||
services.AddGrpcClient<CustomAppService.CustomAppServiceClient>(o =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"]!;
|
||||
var clientKeyPath = config["Service:ClientKey"]!;
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateCustomAppServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
o.Address = new Uri("https://develop");
|
||||
});
|
||||
|
||||
return services;
|
||||
|
@@ -1,55 +0,0 @@
|
||||
using System.Text;
|
||||
using dotnet_etcd.interfaces;
|
||||
using Etcdserverpb;
|
||||
using Google.Protobuf;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public class ServiceRegistry(IEtcdClient etcd, ILogger<ServiceRegistry> logger)
|
||||
{
|
||||
public async Task RegisterService(
|
||||
string serviceName,
|
||||
string serviceUrl,
|
||||
long leaseTtlSeconds = 60,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var key = $"/services/{serviceName}";
|
||||
var leaseResponse = await etcd.LeaseGrantAsync(
|
||||
new LeaseGrantRequest { TTL = leaseTtlSeconds },
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
await etcd.PutAsync(new PutRequest
|
||||
{
|
||||
Key = ByteString.CopyFrom(key, Encoding.UTF8),
|
||||
Value = ByteString.CopyFrom(serviceUrl, Encoding.UTF8),
|
||||
Lease = leaseResponse.ID
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await etcd.LeaseKeepAlive(leaseResponse.ID, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError($"Lease keep-alive failed: {ex.Message}");
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task UnregisterService(string serviceName)
|
||||
{
|
||||
var key = $"/services/{serviceName}";
|
||||
await etcd.DeleteAsync(key);
|
||||
}
|
||||
|
||||
public async Task<string?> GetServiceUrl(string serviceName)
|
||||
{
|
||||
var key = $"/services/{serviceName}";
|
||||
var response = await etcd.GetAsync(key);
|
||||
return response.Kvs.Count == 0 ? null : response.Kvs[0].Value.ToStringUtf8();
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
using dotnet_etcd.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public static class RegistryStartup
|
||||
{
|
||||
public static IServiceCollection AddRegistryService(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration,
|
||||
bool addForwarder = true
|
||||
)
|
||||
{
|
||||
services.AddEtcdClient(options =>
|
||||
{
|
||||
options.ConnectionString = configuration.GetConnectionString("Etcd");
|
||||
options.UseInsecureChannel = configuration.GetValue<bool>("Etcd:Insecure");
|
||||
});
|
||||
services.AddSingleton<ServiceRegistry>();
|
||||
services.AddHostedService<RegistryHostedService>();
|
||||
|
||||
if (addForwarder)
|
||||
services.AddHttpForwarder();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user