Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consul deregistration using hosted service #29

Merged
merged 3 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions ATI.Services.Consul/ConsulDeregistrationExtension.cs
Original file line number Diff line number Diff line change
@@ -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<ConsulRegistrator>();
return builder.MapDelete(deregistrationAddress ?? DeregistrationAddress, async _ => await registrator.DeregisterInstanceAsync());
}
}
16 changes: 8 additions & 8 deletions ATI.Services.Consul/ConsulExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConsulRegistratorOptions>();
services.AddTransient<ConsulInitializer>();
}
services.ConfigureByName<ConsulRegistratorOptions>();
services.AddHostedService<ConsulHostedService>();
services.AddSingleton<ConsulRegistrator>();
}
}
31 changes: 31 additions & 0 deletions ATI.Services.Consul/ConsulHostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
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> consulRegistratorOptions,
ConsulRegistrator registrator) : IHostedService
{
public Task StartAsync(CancellationToken ct)
CptnSnail marked this conversation as resolved.
Show resolved Hide resolved
{
if (!bool.TryParse(ConfigurationManager.AppSettings("ConsulEnabled"), out var enabled) || !enabled)
return Task.CompletedTask;

Console.WriteLine("ConsulHostedService is starting.");
return registrator.RegisterServicesAsync(consulRegistratorOptions.Value,
ConfigurationManager.GetApplicationPort());
}

public Task StopAsync(CancellationToken ct)
{
Console.WriteLine("ConsulHostedService is stopping.");
return registrator.DeregisterInstanceAsync();
}
}
47 changes: 0 additions & 47 deletions ATI.Services.Consul/ConsulInitializer.cs

This file was deleted.

138 changes: 69 additions & 69 deletions ATI.Services.Consul/ConsulRegistrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> _registeredServices = [];

public async Task RegisterServicesAsync(ConsulRegistratorOptions consulRegistratorOptions, int applicationPort)
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static Timer _reregistrationTimer;
private static HashSet<string> 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<string, string>
{
{"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<string, string>
{
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} из консула.");
}
}
}
}
Loading