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 { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { /// <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