如何使用 Consul 自行注册服务
How to self register a service with Consul
我正在尝试 self 在启动时将我的 ASP.NET 核心应用程序注册到 Consul 注册表,并在关闭时注销它。
从 here 我可以了解到调用 http api [put /v1/agent/service/register
] 可能是可行的方法(或者可能不是!)。
在我的应用程序中,我想我将以 Startup
class 为目标,首先添加我的 .json
文件
public Startup(IHostingEnvironment env)
{
var builder = new Configuration().AddJsonFile("consulconfig.json");
Configuration = builder.Build();
}
但是现在,我被卡住了,因为 ConfigureServices
方法告诉我那是我向容器添加服务的地方,而 Configure
方法是我配置 Http 请求管道的地方。
有人能给我指出正确的方向、在线阅读、示例等。
首先我推荐使用Consul.NET
与领事互动。使用它,服务注册可能如下所示:
var registration = new AgentServiceRegistration
{
Name = "foo",
Port = 4242,
Address = "http://bar"
};
using (var client = new ConsulClient())
{
await client.Agent.ServiceRegister(registration);
}
现在让我们借助 DI 和松散耦合将此代码集成到 ASP.NET 核心启动过程中。将您的 json 文件读入 ConsulOptions
实例(没有任何逻辑的 DTO):
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<ConsulOptions>(Configuration);
}
将所有与 Consul 相关的逻辑封装在 class ConsulService
中接受 ConsulOptions
作为依赖:
public class ConsulService : IDisposable
{
public ConsulService(IOptions<ConsulOptions> optAccessor) { }
public void Register()
{
//possible implementation of synchronous API
client.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
}
}
将 class 本身添加到 DI 容器中:
services.AddTransient<ConsulService>();
然后创建IApplicationBuilder
的扩展方法并调用它:
public void Configure(IApplicationBuilder app)
{
app.ConsulRegister();
}
在 ConsulRegister
实现中,我们在应用程序 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
}
}
锁定缺失和静态字段是可以的,因为 Startup
class 在应用程序启动时只执行一次。不要忘记在 OnStopping
方法中取消注册 API!
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace WebApplication1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddConsulConfig(Configuration);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseConsul();
app.UseMvc();
}
}
}
AppExtensions.cs
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1
{
public static class AppExtensions
{
public static IServiceCollection AddConsulConfig(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
{
var address = configuration.GetValue<string>("http://localhost:8500");
consulConfig.Address = new Uri(address);
}));
return services;
}
public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
{
var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();
var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("AppExtensions");
var lifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
// var uri = new Uri(address);
// Better approach should be used to set the below settings. I hard coded just to explain.
var registration = new AgentServiceRegistration()
{
ID = $"MyService",
Name = "WebApplication1",
Address = "localhost", //$"{uri.Host}",
Port = 57084 // uri.Port
};
logger.LogInformation("Registering with Consul");
consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);
lifetime.ApplicationStopping.Register(() =>
{
logger.LogInformation("Unregistering from Consul");
consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
});
return app;
}
}
}
我正在尝试 self 在启动时将我的 ASP.NET 核心应用程序注册到 Consul 注册表,并在关闭时注销它。
从 here 我可以了解到调用 http api [put /v1/agent/service/register
] 可能是可行的方法(或者可能不是!)。
在我的应用程序中,我想我将以 Startup
class 为目标,首先添加我的 .json
文件
public Startup(IHostingEnvironment env)
{
var builder = new Configuration().AddJsonFile("consulconfig.json");
Configuration = builder.Build();
}
但是现在,我被卡住了,因为 ConfigureServices
方法告诉我那是我向容器添加服务的地方,而 Configure
方法是我配置 Http 请求管道的地方。
有人能给我指出正确的方向、在线阅读、示例等。
首先我推荐使用Consul.NET 与领事互动。使用它,服务注册可能如下所示:
var registration = new AgentServiceRegistration
{
Name = "foo",
Port = 4242,
Address = "http://bar"
};
using (var client = new ConsulClient())
{
await client.Agent.ServiceRegister(registration);
}
现在让我们借助 DI 和松散耦合将此代码集成到 ASP.NET 核心启动过程中。将您的 json 文件读入 ConsulOptions
实例(没有任何逻辑的 DTO):
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<ConsulOptions>(Configuration);
}
将所有与 Consul 相关的逻辑封装在 class ConsulService
中接受 ConsulOptions
作为依赖:
public class ConsulService : IDisposable
{
public ConsulService(IOptions<ConsulOptions> optAccessor) { }
public void Register()
{
//possible implementation of synchronous API
client.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
}
}
将 class 本身添加到 DI 容器中:
services.AddTransient<ConsulService>();
然后创建IApplicationBuilder
的扩展方法并调用它:
public void Configure(IApplicationBuilder app)
{
app.ConsulRegister();
}
在 ConsulRegister
实现中,我们在应用程序 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
}
}
锁定缺失和静态字段是可以的,因为 Startup
class 在应用程序启动时只执行一次。不要忘记在 OnStopping
方法中取消注册 API!
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace WebApplication1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddConsulConfig(Configuration);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseConsul();
app.UseMvc();
}
}
}
AppExtensions.cs
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1
{
public static class AppExtensions
{
public static IServiceCollection AddConsulConfig(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
{
var address = configuration.GetValue<string>("http://localhost:8500");
consulConfig.Address = new Uri(address);
}));
return services;
}
public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
{
var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();
var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("AppExtensions");
var lifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
// var uri = new Uri(address);
// Better approach should be used to set the below settings. I hard coded just to explain.
var registration = new AgentServiceRegistration()
{
ID = $"MyService",
Name = "WebApplication1",
Address = "localhost", //$"{uri.Host}",
Port = 57084 // uri.Port
};
logger.LogInformation("Registering with Consul");
consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);
lifetime.ApplicationStopping.Register(() =>
{
logger.LogInformation("Unregistering from Consul");
consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
});
return app;
}
}
}