Consul 服务的注册和发现
Consul 是Hashicorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。Consul是分布式的,高可用的,可横向扩展的。
Consul 的主要特点有:
Service Discovery : 服务注册与发现,Consul 的客户端可以做为一个服务注册到 Consul,也可以通过 Consul 来查找特定的服务提供者,并且根据提供的信息进行调用。
Health Checking: Consul 客户端会定期发送一些健康检查数据和服务端进行通讯,判断客户端的状态、内存使用情况是否正常,用来监控整个集群的状态,防止服务转发到故障的服务上面。
KV Store: Consul 还提供了一个容易使用的键值存储。这可以用来保持动态配置,协助服务协调、建立 Leader 选举,以及开发者想构造的其它一些事务。
Secure Service Communication: Consul 可以为服务生成分布式的 TLS 证书,以建立相互的 TLS 连接。 可以使用 intentions 定义允许哪些服务进行通信。 可以使用 intentions 轻松管理服务隔离,而不是使用复杂的网络拓 扑和静态防火墙规则。
Multi Datacenter: Consul 支持开箱即用的多数据中心,这意味着用户不需要担心需要建立额外的抽象层让业务扩展到多个区域。
Consul 角色
Server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其它数据中心通讯。 每个数据中心的 Server 数量推荐为 3 个或是 5 个。
Client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群。
Consul 旨在对 DevOps 社区和应用程序开发人员友好,使其成为现代、弹性基础架构的理想选择。
首先下载Consul服务组件:官网下载:https://www.consul.io/downloads
启动Consul:
根据启动后的http显示端口8500端口:
通过浏览器查看 Consul服务中心是否启动成功,地址:http://localhost:8500,如果成功,效果如下:
然后创建一个服务端实例工程:
添加必要的测试类:
using System; using System.Collections.Generic; using System.Text; namespace Demo { /// <summary> /// 用户模型。 /// </summary> public class User { /// <summary> /// 获取或者设置用户主键。 /// </summary> public int ID { get; set; } /// <summary> /// 获取或者设置用户姓名。 /// </summary> public string Name { get; set; } /// <summary> /// 获取或者设置用户账号名称。 /// </summary> public string Account { get; set; } /// <summary> /// 获取或者设置用户密码。 /// </summary> public string Password { get; set; } /// <summary> /// 获取或者设置用户的电子邮箱地址。 /// </summary> public string Email { get; set; } /// <summary> /// 获取或者设置用户角色。 /// </summary> public string Role { get; set; } /// <summary> /// 获取或者设置用户的登录时间。 /// </summary> public DateTime LoginTime { get; set; } } }
using System; using System.Collections.Generic; using System.Text; namespace Demo { /// <summary> /// 用户服务的接口定义。 /// </summary> public interface IUserService { /// <summary> /// 查找指定主键的用户实例对象。 /// </summary> /// <param name="id">用户的主键。</param> /// <returns>返回查找到的用户实例对象。</returns> User FindUser(int id); /// <summary> /// 获取所有用户的实例集合。 /// </summary> /// <returns>返回所有的用户实例。</returns> IEnumerable<User> UserAll(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Demo { /// <summary> /// 实现用户服务接口的实现类型。 /// </summary> public class UserService : IUserService { private IList<User> dataList; /// <summary> /// 初始化类型的实例 /// </summary> public UserService() { dataList = new List<User>() { new User {ID=1,Name="张三",Account="5435435345",Password="4535435435",Email="4535345345", Role="Admin", LoginTime=DateTime.Now}, new User {ID=2,Name="李四",Account="5435435345",Password="5435345345",Email="543534535", Role="Admin", LoginTime=DateTime.Now.AddDays(-5) }, new User { ID = 3, Name = "王二", Account = "45354", Password = "3245345", Email = "54353455", Role = "Admin", LoginTime = DateTime.Now.AddDays(-30) }, new User { ID = 4, Name = "麻子", Account = "45354", Password = "4534535", Email = "453534534", Role = "Admin", LoginTime = DateTime.Now.AddDays(-90) }, new User { ID = 5, Name = "陈五", Account = "54353", Password = "5435345", Email = "5435345345", Role = "Admin", LoginTime = DateTime.Now.AddMinutes(-50) } }; } /// <summary> /// 查找指定主键的用户实例对象。 /// </summary> /// <param name="id">用户的主键。</param> /// <returns>返回查找到的用户实例对象。</returns> public User FindUser(int id) { return dataList.FirstOrDefault(user => user.ID == id); } /// <summary> /// 获取所有用户的实例集合。 /// </summary> /// <returns>返回所有的用户实例。</returns> public IEnumerable<User> UserAll() { return dataList; } } }
using Consul; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Text; namespace Demo { /// <summary> /// Consul 静态扩展类。 /// </summary> public static class ConsulExtension { /// <summary> ///类型初始化器,初始化 Consul 网址和数据中心。 /// </summary> static ConsulExtension() { Uri = new Uri("http://localhost:8500"); DataCenter = "dc1"; } /// <summary> /// 获取或者设置 Consul 的网址。 /// </summary> public static Uri Uri { get; set; } /// <summary> /// 获取或者设置数据中心。 /// </summary> public static string DataCenter { get; set; } /// <summary> /// 向 Consul 服务中心注册服务实例。 /// </summary> /// <param name="configuration">配置对象。</param> /// <param name="consulServiceName">在 Consul 服务中心注册的服务类别名称,多个实例的 ID 可以属于一个服务类别名称。</param> /// <param name="serviceID">服务实例的主键值,必须唯一。</param> public static void ConsulRegist(this IConfiguration configuration, string consulServiceName, string serviceID) { if (string.IsNullOrEmpty(consulServiceName) || string.IsNullOrWhiteSpace(consulServiceName)) { throw new ArgumentNullException("consulServiceName is null"); } if (string.IsNullOrEmpty(serviceID) || string.IsNullOrWhiteSpace(serviceID)) { throw new ArgumentNullException("serviceID is null."); } using (ConsulClient client = new ConsulClient(config => { config.Address = Uri; config.Datacenter = DataCenter; })) { string ip = configuration["ip"]; int port = int.Parse(configuration["port"]); int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]); client.Agent.ServiceRegister(new AgentServiceRegistration() { ID = serviceID, Name = consulServiceName, Address = ip, Port = port, Tags = new string[] { weight.ToString() }, Check = new AgentServiceCheck() { Interval = TimeSpan.FromSeconds(12), HTTP = $"http://{ip}:{port}/API/Health/Index", Timeout = TimeSpan.FromSeconds(5), DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20) } }).Wait(); Console.WriteLine($"注册服务:{ip}:{port}--Weight:{weight}"); }; } /// <summary> /// 向 Consul 服务中心注销服务实例。 /// </summary> /// <param name="applicationLifetime">配置对象。</param> /// <param name="serviceID">服务实例的主键值,必须唯一。</param> public static void ConsulDown(this IApplicationLifetime applicationLifetime, string serviceID) { if (string.IsNullOrEmpty(serviceID) || string.IsNullOrWhiteSpace(serviceID)) { throw new ArgumentNullException("serviceID is null"); } applicationLifetime.ApplicationStopped.Register(() => { using (var consulClient = new ConsulClient(config => { config.Address = Uri; config.Datacenter = DataCenter; })) { Console.WriteLine("服务已经退出"); consulClient.Agent.ServiceDeregister(serviceID); } }); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; namespace ConsulAPIDemo.Controllers { [Route("api/[controller]")] [ApiController] public class HealthController : ControllerBase { private IConfiguration _configuration; /// <summary> /// 初始化该类型的新实例。 /// </summary> /// <param name="configuration">配置接口。</param> public HealthController(IConfiguration configuration) { _configuration = configuration; } /// <summary> /// 要调用的接口。 /// </summary> [HttpGet] [Route("Index")] public IActionResult Index() { Console.WriteLine($"This is HealhController {_configuration["port"]} Invoke"); return Ok(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Demo; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace ConsulAPIDemo.Controllers { /// <summary> /// 用户的 API 类型。 /// </summary> [Route("api/[controller]")] [ApiController] public class UsersController : ControllerBase { #region 私有字段 private readonly ILogger<UsersController> _logger; private readonly IUserService _userService; private IConfiguration _configuration; #endregion #region 构造函数 /// <summary> /// 初始化该类型的新实例。 /// </summary> /// <param name="logger">日志记录器。</param> /// <param name="userService">用户服务接口。</param> /// <param name="configuration">配置服务。</param> public UsersController(ILogger<UsersController> logger, IUserService userService, IConfiguration configuration) { _logger = logger; _userService = userService; _configuration = configuration; } #endregion #region 实例方法 /// <summary> /// 获取一条记录 /// </summary> /// <param name="id"></param> /// <returns></returns> [HttpGet] [Route("Get")] public User Get(int id) { return _userService.FindUser(id); } /// <summary> /// 获取所有记录。 /// </summary> /// <returns></returns> [HttpGet] [Route("All")] //[Authorize] public IEnumerable<User> Get() { Console.WriteLine($"This is UsersController {this._configuration["port"]} Invoke"); return this._userService.UserAll().Select((user => new User { ID = user.ID, Name = user.Name, Account = user.Account, Password = user.Password, Email = user.Email, Role = $"{this._configuration["ip"]}:{this._configuration["port"]}", LoginTime = user.LoginTime })); ; } /// <summary> /// 超时处理 /// </summary> /// <returns></returns> [HttpGet] [Route("Timeout")] public IEnumerable<User> Timeout() { Console.WriteLine($"This is Timeout Start"); //超时设置。 Thread.Sleep(3000); Console.WriteLine($"This is Timeout End"); return this._userService.UserAll().Select((user => new User { ID = user.ID, Name = user.Name, Account = user.Account, Password = user.Password, Email = user.Email, Role = $"{this._configuration["ip"]}:{this._configuration["port"]}", LoginTime = user.LoginTime })); ; } #endregion } }
startup里面的配置:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Demo; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace ConsulAPIDemo { 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.AddSingleton<IUserService, UserService>(); 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, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, Microsoft.Extensions.Hosting.IApplicationLifetime applicationLifetime) { 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(); } #region Consul 注册,需要增加的 string serviceID = $"Service:{Configuration["ip"]}:{Configuration["port"]}---{Guid.NewGuid()}"; string consuleServiceName = "PatrickLiuService"; //注册服务。 Configuration.ConsulRegist(consuleServiceName, serviceID); //注销服务 applicationLifetime.ConsulDown(serviceID); #endregion app.UseHttpsRedirection(); app.UseMvc(); } } }
编译一下,根据不同端口启动api接口服务:
API端口设置为2000:
API端口设置为3000:
注册四个实例:
上面整个过程已经完成了服务的注册,至于发现自己可以测一下,自己关掉一个服务,Consul会自动检查各个服务的健康度,如果哪个服务出现异常,consul会自动剔除异常服务。
参考文档:https://www.cnblogs.com/PatrickLiu/p/13939135.html