Search code examples
c#asp.net-coremicroservicesconsul

How to self register a service with Consul


I'm trying to self register my ASP.NET Core application to Consul registry on startup and deregister it on shutdown.

From here I can gather that calling the http api [put /v1/agent/service/register] might be the way to go (or maybe not!).

From my app, I thought I'll target the Startup class, starting with adding the my .json file

public Startup(IHostingEnvironment env)
{
   var builder = new Configuration().AddJsonFile("consulconfig.json");
   Configuration = builder.Build();
}

But now, I'm stuck as ConfigureServices method tells me thats where I add services to the container, and Configure method is where I configure the Http request pipeline.

Anybody to point me in the right directions, online readings, examples, etc.


Solution

  • First of all I recommend to use Consul.NET to interact with Consul. Using it, a service registration may look like:

    var registration = new AgentServiceRegistration
    {
        Name = "foo",
        Port = 4242,
        Address = "http://bar"
    };
    
    using (var client = new ConsulClient())
    {
        await client.Agent.ServiceRegister(registration);
    }
    

    Now let's integrate this code into ASP.NET Core startup process with help of DI and loose coupling. Read your json file into ConsulOptions instance (DTO without any logic):

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOptions();
        services.Configure<ConsulOptions>(Configuration);
    }
    

    Encapsulate all Consul-related logic in class ConsulService accepting ConsulOptions as a dependency:

    public class ConsulService : IDisposable
    {
        public ConsulService(IOptions<ConsulOptions> optAccessor) { }
    
        public void Register() 
        {
            //possible implementation of synchronous API
            client.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
        }
    }
    

    Add the class itself to the DI container:

    services.AddTransient<ConsulService>();
    

    Then create an extention method of IApplicationBuilder and call it:

    public void Configure(IApplicationBuilder app)
    {
        app.ConsulRegister();
    }
    

    In ConsulRegister implementation we add our hooks on application start/stop:

    public static class ApplicationBuilderExtensions
    {
        public static ConsulService Service { get; set; }
    
        public static IApplicationBuilder ConsulRegister(this IApplicationBuilder app)
        {
            //design ConsulService class as long-lived or store ApplicationServices instead
            Service = app.ApplicationServices.GetService<ConsulService>();
    
            var life = app.ApplicationServices.GetService<IApplicationLifetime>();
    
            life.ApplicationStarted.Register(OnStarted);
            life.ApplicationStopping.Register(OnStopping);
    
            return app;
        }
    
        private static void OnStarted()
        {
            Service.Register(); //finally, register the API in Consul
        }
    }
    

    Locking absence and static fields are OK because the Startup class is executed exactly once on application start. Don't forget to de-register the API in OnStopping method!