Consul简介
- Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,比如 Airbnb的SmartStack等相比,Consul的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等),使用起来也较为简单。
- 要想利用Consul提供的服务实现服务的注册与发现,我们需要建立Consul Cluster。在Consul方案中,每个提供服务的节点上都要部署和运行Consul的Client Agent,所有运行Consul Agent节点的集合构成Consul Cluster。Consul Agent有两种运行模式:Server和Client。这里的Server和Client只是Consul集群层面的区分,与搭建在Cluster之上的应用服务无关。以Server模式运行的Consul Agent节点用于维护Consul集群的状态,官方建议每个Consul Cluster至少有3个或以上的运行在Server Mode的Agent,Client节点不限。
- Consul支持多数据中心,每个数据中心的Consul Cluster都会在运行于Server模式下的Agent节点中选出一个Leader节点,这个选举过程通过Consul实现的raft协议保证,多个 Server节点上的Consul数据信息是强一致的。处于Client Mode的Consul Agent节点比较简单,无状态,仅仅负责将请求转发给Server Agent节点。
- Consul的架构以及相关的角色,如下图所示:
安装Consul
- 官网下载consul安装包:https://www.consul.io/downloads.html
- 本文使用windows环境,下载64位win版本
- 解压文件,使用命令到consul.exe目录下,执行.\consul.exe agent -dev
- 访问:http://localhost:8500/ui/dc1/services
创建.net core 服务项目
- 是用.net core 3.1 创建默认api项目模板代码,做简单的修改,这只是一个简单的api接口示例。
[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[HttpGet]
public string Get()
{
var summary = $"天气:{Summaries[new Random().Next(Summaries.Length)]};服务地址:{Request.Host}";
Console.WriteLine(summary);
return summary;
}
- Nuget添加Consul的引用
- 添加配置文件consulconfig.json,以及对应的类
{
"ConsulAddress": "http://127.0.0.1:8500",
"ServiceName": "MicrServiceDemo",
"HealthCheck": "/Health"
}
public class ConsulServiceOptions
{
public string ConsulAddress { get; set; }
public string ServiceId { get; set; }
public string ServiceName { get; set; }
public string HealthCheck { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddHealthChecks();
services.Configure<ConsulServiceOptions>(new ConfigurationBuilder()
.AddJsonFile("consulconfig.json").Build());
}
public static class ConsulRegistrationExtensions
{
public static IApplicationBuilder UseConsul(this IApplicationBuilder app, IConfiguration configuration)
{
var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
var serviceOptions = app.ApplicationServices.GetRequiredService<IOptions<ConsulServiceOptions>>().Value;
serviceOptions.ServiceId = Guid.NewGuid().ToString("N");
var consulClient = new ConsulClient(configuration =>
{
configuration.Address = new Uri(serviceOptions.ConsulAddress);
});
var features = app.Properties["server.Features"] as FeatureCollection;
Uri uri = null;
var address = features.Get<IServerAddressesFeature>().Addresses?.FirstOrDefault();
if (address == null)
{
uri = new Uri(configuration["urls"]);
}
else
{
uri = new Uri(address);
}
var registration = new AgentServiceRegistration()
{
ID = serviceOptions.ServiceId,
Name = serviceOptions.ServiceName,
Address = uri.Host,
Port = uri.Port,
Check = new AgentServiceCheck
{
Timeout = TimeSpan.FromSeconds(5),
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
HTTP = $"{uri.Scheme}://{uri.Host}:{uri.Port}{serviceOptions.HealthCheck}",
Interval = TimeSpan.FromSeconds(10),
}
};
consulClient.Agent.ServiceRegister(registration).Wait();
lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(serviceOptions.ServiceId).Wait();
});
return app;
}
}
- 在Startup.Configure方法中,添加以下代码
var serviceOptions = app.ApplicationServices.GetRequiredService<IOptions<ConsulServiceOptions>>();
app.UseHealthChecks(serviceOptions.Value.HealthCheck);
app.UseConsul(Configuration);
- 启动服务,查看服务是否注册成功,如下如所示,服务正常注册,且状态为健康状态。
- 用不同的端口,启用多个服务,这里我是用命令启动:dotnet MicrServiceDemo.dll --urls=“http://localhost:6001”
- 添加以下代码,发现已注册的健康服务
[HttpGet]
[Route("/GetAllServices")]
public async Task<List<string>> GetAllServicesAsync()
{
var consuleClient = new ConsulClient(consulConfig =>
{
consulConfig.Address = new Uri("http://127.0.0.1:8500");
});
var queryResult = await consuleClient.Health.Service("MicrServiceDemo", "", true);
var result = new List<string>();
foreach (var service in queryResult.Response)
{
result.Add(service.Service.Address + ":" + service.Service.Port);
}
return result;
}
一个简单的Consul服务注册及发现代码示例已完成,下一篇更新配合Consul使用Ocelot。
示例代码