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