From da30775846052a1325c819a148b6003b02fdaa5a Mon Sep 17 00:00:00 2001 From: Aidar Shaikhiev Date: Thu, 5 Sep 2024 20:42:48 +0500 Subject: [PATCH] Consul deregistration using hosted service --- .../ConsulDeregistrationExtension.cs | 30 ++-- ATI.Services.Consul/ConsulExtensions.cs | 16 +- ATI.Services.Consul/ConsulHostedService.cs | 25 ++++ ATI.Services.Consul/ConsulInitializer.cs | 47 ------ ATI.Services.Consul/ConsulRegistrator.cs | 138 +++++++++--------- 5 files changed, 114 insertions(+), 142 deletions(-) create mode 100644 ATI.Services.Consul/ConsulHostedService.cs delete mode 100644 ATI.Services.Consul/ConsulInitializer.cs diff --git a/ATI.Services.Consul/ConsulDeregistrationExtension.cs b/ATI.Services.Consul/ConsulDeregistrationExtension.cs index b3777ef..0bb6767 100644 --- a/ATI.Services.Consul/ConsulDeregistrationExtension.cs +++ b/ATI.Services.Consul/ConsulDeregistrationExtension.cs @@ -1,25 +1,19 @@ -using System.Threading.Tasks; -using JetBrains.Annotations; +using JetBrains.Annotations; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; -namespace ATI.Services.Consul -{ - [PublicAPI] - public static class ConsulDeregistrationExtension - { - private const string DeregistrationAddress = "_internal/consul/deregister"; +namespace ATI.Services.Consul; - public static IEndpointConventionBuilder MapConsulDeregistration(this IEndpointRouteBuilder builder, - string deregistrationAddress = null) - { - return builder.MapDelete(deregistrationAddress ?? DeregistrationAddress, DeregisterDelegate); - } +[PublicAPI] +public static class ConsulDeregistrationExtension +{ + private const string DeregistrationAddress = "_internal/consul/deregister"; - private static async Task DeregisterDelegate(HttpContext _) - { - await ConsulRegistrator.DeregisterInstanceAsync(); - } + public static IEndpointConventionBuilder MapConsulDeregistration(this IEndpointRouteBuilder builder, + string deregistrationAddress = null) + { + var registrator = builder.ServiceProvider.GetService(); + return builder.MapDelete(deregistrationAddress ?? DeregistrationAddress, async _ => await registrator.DeregisterInstanceAsync()); } } \ No newline at end of file diff --git a/ATI.Services.Consul/ConsulExtensions.cs b/ATI.Services.Consul/ConsulExtensions.cs index c931cd7..7e57b16 100644 --- a/ATI.Services.Consul/ConsulExtensions.cs +++ b/ATI.Services.Consul/ConsulExtensions.cs @@ -2,15 +2,15 @@ using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; -namespace ATI.Services.Consul +namespace ATI.Services.Consul; + +public static class ConsulExtensions { - public static class ConsulExtensions + [PublicAPI] + public static void AddConsul(this IServiceCollection services) { - [PublicAPI] - public static void AddConsul(this IServiceCollection services) - { - services.ConfigureByName(); - services.AddTransient(); - } + services.ConfigureByName(); + services.AddHostedService(); + services.AddSingleton(); } } \ No newline at end of file diff --git a/ATI.Services.Consul/ConsulHostedService.cs b/ATI.Services.Consul/ConsulHostedService.cs new file mode 100644 index 0000000..961a030 --- /dev/null +++ b/ATI.Services.Consul/ConsulHostedService.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; +using ATI.Services.Common.Behaviors; +using JetBrains.Annotations; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; + +namespace ATI.Services.Consul; + +[PublicAPI] +public class ConsulHostedService( + IOptions consulRegistratorOptions, + ConsulRegistrator registrator) : IHostedService +{ + public Task StartAsync(CancellationToken ct) + { + if (bool.TryParse(ConfigurationManager.AppSettings("ConsulEnabled"), out var enabled) && enabled) + return registrator.RegisterServicesAsync(consulRegistratorOptions.Value, + ConfigurationManager.GetApplicationPort()); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken ct) => registrator.DeregisterInstanceAsync(); +} \ No newline at end of file diff --git a/ATI.Services.Consul/ConsulInitializer.cs b/ATI.Services.Consul/ConsulInitializer.cs deleted file mode 100644 index ec3e9e5..0000000 --- a/ATI.Services.Consul/ConsulInitializer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Threading.Tasks; -using ATI.Services.Common.Behaviors; -using ATI.Services.Common.Initializers; -using ATI.Services.Common.Initializers.Interfaces; -using JetBrains.Annotations; -using Microsoft.Extensions.Options; - -namespace ATI.Services.Consul -{ - [PublicAPI] - [InitializeOrder(Order = InitializeOrder.Sixth)] - public class ConsulInitializer : IInitializer - { - private static bool _initialized; - private readonly ConsulRegistratorOptions _consulRegistratorOptions; - - public ConsulInitializer(IOptions consulRegistratorOptions) - { - _consulRegistratorOptions = consulRegistratorOptions.Value; - } - - public async Task InitializeAsync() - { - if (_initialized) - return; - - string consulEnabledString; - if ((consulEnabledString = ConfigurationManager.AppSettings("ConsulEnabled")) == null - || bool.TryParse(consulEnabledString, out var consulEnabled) && consulEnabled) - { - await ConsulRegistrator.RegisterServicesAsync(_consulRegistratorOptions, ConfigurationManager.GetApplicationPort()); - } - - _initialized = true; - } - - public string InitStartConsoleMessage() - { - return "Start Consul initializer"; - } - - public string InitEndConsoleMessage() - { - return $"End Consul initializer, result {_initialized}"; - } - } -} \ No newline at end of file diff --git a/ATI.Services.Consul/ConsulRegistrator.cs b/ATI.Services.Consul/ConsulRegistrator.cs index 3305004..13e6c0f 100644 --- a/ATI.Services.Consul/ConsulRegistrator.cs +++ b/ATI.Services.Consul/ConsulRegistrator.cs @@ -7,93 +7,93 @@ using Newtonsoft.Json; using NLog; -namespace ATI.Services.Consul +namespace ATI.Services.Consul; + +public class ConsulRegistrator { - public static class ConsulRegistrator + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private Timer _reregistrationTimer; + private readonly HashSet _registeredServices = []; + + public async Task RegisterServicesAsync(ConsulRegistratorOptions consulRegistratorOptions, int applicationPort) { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - private static Timer _reregistrationTimer; - private static HashSet RegisteredServices { get; set; } = new(); + foreach (var consulServiceOptions in consulRegistratorOptions.ConsulServiceOptions) + { + consulServiceOptions.Check.HTTP = $"http://localhost:{applicationPort}{consulServiceOptions.Check.HTTP}"; + await DeregisterFromConsulAsync($"{consulServiceOptions.ServiceName}-{Dns.GetHostName()}-{applicationPort}"); + } - public static async Task RegisterServicesAsync(ConsulRegistratorOptions consulRegistratorOptions, int applicationPort) + if(_reregistrationTimer != null) + await _reregistrationTimer.DisposeAsync(); + + _reregistrationTimer = new Timer(async _ => await RegisterServicesAsyncPrivate(consulRegistratorOptions, applicationPort), + null, + TimeSpan.FromSeconds(0), + consulRegistratorOptions.ReregistrationPeriod); + } + + private async Task RegisterServicesAsyncPrivate(ConsulRegistratorOptions consulRegistratorOptions, int applicationPort) + { + try { foreach (var consulServiceOptions in consulRegistratorOptions.ConsulServiceOptions) - { - consulServiceOptions.Check.HTTP = $"http://localhost:{applicationPort}{consulServiceOptions.Check.HTTP}"; - await DeregisterFromConsulAsync($"{consulServiceOptions.ServiceName}-{Dns.GetHostName()}-{applicationPort}"); - } - - _reregistrationTimer = new Timer(async _ => await RegisterServicesAsyncPrivate(consulRegistratorOptions, applicationPort), - null, - TimeSpan.FromSeconds(0), - consulRegistratorOptions.ReregistrationPeriod); + await RegisterToConsulAsync(consulServiceOptions, applicationPort); } - - private static async Task RegisterServicesAsyncPrivate(ConsulRegistratorOptions consulRegistratorOptions, int applicationPort) + catch (Exception e) { - try - { - foreach (var consulServiceOptions in consulRegistratorOptions.ConsulServiceOptions) - await RegisterToConsulAsync(consulServiceOptions, applicationPort); - } - catch (Exception e) - { - Logger.Error(e); - } + _logger.Error(e); } + } - private static async Task RegisterToConsulAsync(ConsulServiceOptions options, int applicationPort) - { - var serviceId = $"{options.ServiceName}-{Dns.GetHostName()}-{applicationPort}"; - RegisteredServices.Add(serviceId); + private async Task RegisterToConsulAsync(ConsulServiceOptions options, int applicationPort) + { + var serviceId = $"{options.ServiceName}-{Dns.GetHostName()}-{applicationPort}"; + _registeredServices.Add(serviceId); - var swaggerUrls = JsonConvert.SerializeObject(options.SwaggerUrls); + var swaggerUrls = JsonConvert.SerializeObject(options.SwaggerUrls); - using var client = new ConsulClient(); - var cr = new AgentServiceRegistration - { - Name = options.ServiceName, - ID = serviceId, - Tags = options.Tags, - Check = options.Check, - Port = applicationPort, - Meta = new Dictionary - { - {"swagger_urls", swaggerUrls} - } - }; - await client.Agent.ServiceRegister(cr); - } - - public static async Task DeregisterInstanceAsync() + using var client = new ConsulClient(); + var cr = new AgentServiceRegistration { - await _reregistrationTimer.DisposeAsync(); - try - { - foreach (var serviceId in RegisteredServices) - { - await DeregisterFromConsulAsync(serviceId); - } - } - catch (Exception e) + Name = options.ServiceName, + ID = serviceId, + Tags = options.Tags, + Check = options.Check, + Port = applicationPort, + Meta = new Dictionary { - Logger.Error(e); + {"swagger_urls", swaggerUrls} } - } + }; + await client.Agent.ServiceRegister(cr); + } - private static async Task DeregisterFromConsulAsync(string serviceId) + public async Task DeregisterInstanceAsync() + { + await _reregistrationTimer.DisposeAsync(); + try { - try - { - using var client = new ConsulClient(); - await client.Agent.ServiceDeregister(serviceId); - } - catch (Exception ex) + foreach (var serviceId in _registeredServices) { - Logger.Error(ex, $"Не удалось дерегистрировать {serviceId} из консула."); + await DeregisterFromConsulAsync(serviceId); } } + catch (Exception e) + { + _logger.Error(e); + } + } - + private async Task DeregisterFromConsulAsync(string serviceId) + { + try + { + using var client = new ConsulClient(); + await client.Agent.ServiceDeregister(serviceId); + } + catch (Exception ex) + { + _logger.Error(ex, $"Не удалось дерегистрировать {serviceId} из консула."); + } } -} +} \ No newline at end of file