Consul是一种提供具有服务发现,配置和分段功能的全功能控制平面的服务网格解决方案,其主要功能有:
- 服务发现:Consul的客户端可以注册服务,例如api或mysql,其他客户端可以使用Consul来发现给定服务的提供者。使用DNS或HTTP,应用程序可以轻松找到它们所依赖的服务。
- 健康监测:Consul客户端可以提供与给定服务相关的任意数量的健康检查,可以使用此信息来监控集群运行状况,服务发现组件使用该信息将流量路由远离不健康的主机。
- 键值存储:应用程序可以将Consul的分层键/值存储用于任何目的,包括动态配置,功能标记,协调,领导者选举等。简单的HTTP API使其易于使用。
- Secure Service Communication: Consul可以为服务生成和分发TLS证书,以建立相互的TLS连接。意图可用于定义允许哪些服务进行通信。可以使用可以实时更改的意图轻松管理服务分段,而不是使用复杂的网络拓扑和静态防火墙规则。
- 多数据中心:Consul支持多个数据中心。这意味着Consul的用户不必担心构建额外的抽象层以扩展到多个区域。
具体关于Consul的介绍可以查看官网:https://www.consul.io/docs/index.html 。这里介绍在我们的项目中使用到的服务注册与发现功能。
首先下载对应平台的consul进行安装,笔者使用windows 64位,具体下载链接:https://www.consul.io/downloads.html 。windows下载下来就是一个consul.exe文件,可以运行下面的命令来启动consul服务。
consul.exe agent --dev
浏览器访问 http://localhost:8500/ui/dc1/services 可以看到Consul的界面
安装完Consul之后,如何在User.Identity中找到User.Api呢,接着往下看。
要让User.Identity找到,需要将User.Api注册到Consul中,首先在User.Api项目中添加Consul的Nuget包。
install-package Consul
在appsettings.json中添加配置
"ServiceDiscovery": { "ServiceName": "userapi", "Consul": { "HttpEndpoint": "http://127.0.0.1:8500", "DnsEndpoint": { "Address": "127.0.0.1", "Port": 8600 } } }
为了读取到该配置,需要创建一个ServiceDiscoveryOptions、ConsulOptions、DnsEndpoint来建立映射。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; namespace User.API.Dtos { public class DnsEndpoint { public string Address { get; set; } public int Port { get; set; } public IPEndPoint ToIPEndPoint() { return new IPEndPoint(IPAddress.Parse(Address), Port); } } }
namespace User.API.Dtos { public class ConsulOptions { public string HttpEndpoint { get; set; } public DnsEndpoint DnsEndpoint { get; set; } } }
namespace User.API.Dtos { public class ServiceDiscoveryOptions { public string ServiceName { get; set; } public ConsulOptions Consul { get; set; } } }
接着在Startup.cs中添加注入
services.AddOptions(); services.Configure<ServiceDiscoveryOptions>(Configuration.GetSection("ServiceDiscovery")); services.AddSingleton<IConsulClient>(p => new ConsulClient(cfg => { var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value; if (!string.IsNullOrEmpty(serviceConfiguration.Consul.HttpEndpoint)) { // if not configured, the client will use the default value "127.0.0.1:8500" cfg.Address = new Uri(serviceConfiguration.Consul.HttpEndpoint); } }));
最后就是在User.Api启动时将其注册到Consul中,停止时删除注册了,代码如下
public void Configure(IApplicationBuilder app ,IHostingEnvironment env ,IApplicationLifetime applicationLifetime ,ILoggerFactory loggerFactory ,IOptions<ServiceDiscoveryOptions> serviceOptions ,IConsulClient consul) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); }//启动时向consul注册服务 applicationLifetime.ApplicationStarted.Register(()=> { RegisterService(app, serviceOptions, consul, applicationLifetime); }); app.UseMvc(); } /// <summary> /// 注册服务 /// </summary> /// <param name="app"></param> /// <param name="serviceOptions"></param> /// <param name="consul"></param> /// <param name="applicationLifetime"></param> private void RegisterService(IApplicationBuilder app, IOptions<ServiceDiscoveryOptions> serviceOptions, IConsulClient consul, IApplicationLifetime applicationLifetime) { var features = app.Properties["server.Features"] as FeatureCollection; var addresses = features.Get<IServerAddressesFeature>() .Addresses .Select(p => new Uri(p)); foreach (var address in addresses) { var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}"; var httpCheck = new AgentServiceCheck() { DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1), Interval = TimeSpan.FromSeconds(30), HTTP = new Uri(address, "HealthCheck").OriginalString }; var registration = new AgentServiceRegistration() { Checks = new[] { httpCheck }, Address = address.Host, ID = serviceId, Name = serviceOptions.Value.ServiceName, Port = address.Port }; consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult(); //停止时向consul清除该服务 applicationLifetime.ApplicationStopping.Register(() => { DeRegisterService(consul, serviceId); }); } } /// <summary> /// 清除服务 /// </summary> /// <param name="consul"></param> /// <param name="serviceId"></param> private void DeRegisterService(IConsulClient consul,string serviceId) { consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult(); }
为了consul可以监控user.api的运行情况,在User.Api中创建一个controller
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; namespace User.API.Controllers { [Route("[Controller]")] public class HealthCheckController : Controller { [HttpGet("")] [HttpHead("")] public void Get() { } } }
到这里,User.Api注册成功了,接下来就是要在User.Identity中调用User.Api了,我们将用到Polly做熔断重试,下篇再讲。