Consul初探-服務註冊和發現
前言
經過上一篇的學習,現在已經來到了服務註冊發現環節;Consul 的核心功能就是服務註冊和發現,Consul 客戶端通過將自己註冊到 Consul 伺服器叢集,然後等待呼叫方去發現服務,實現代理轉發到真正的業務系統,還可以基於服務發現做負載均衡,甚至可以在客戶端請求到底服務之前進行攔截,做一些基礎性的工作,比如身份驗證、限流、熔斷等等業務系統的前瞻性工作。
服務註冊
引用 Consul.net 客戶端
在 .NETCore 平臺下,可以使用 Consul 的客戶端元件,使其嵌入到業務系統中,完成服務自動註冊、健康檢查等工作,為了使用這些自動化的功能,需要在專案中進行 nuget 包引用
截止本文發文時,Consul 的 NETStandard 最新版本是 0.7.2.6,從版本號來看,更新的頻率非常頻繁,但是 Github 上的 star 數量並不多,這就表示 .NETCore 社群在 Consul 的關注度上還是非常小眾的。
改造 Program.cs 程式入口
為了使用服務執行時偵聽的地址和埠作為 Consul 健康檢查的地址,需要對 Program.cs 進行簡單的改造,程式碼如下:
public static IWebHost BuildWebHost(string[] args) { var config = new ConfigurationBuilder().AddCommandLine(args).Build(); var url = $"{config["scheme"]}://{config["ip"]}:{config["port"]}"; return WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseConfiguration(config) .UseUrls(url) .Build(); }
上面的程式碼將命令列的引數編譯為配置檔案物件,這些引數為了方便除錯,我一次性的寫入了Properties\launchSettings.json 檔案中,如下圖
在 launchSettings.json 檔案中的表現形式為:
{ "profiles": { "Ron.Consul": { "commandName": "Project", "commandLineArgs": "--scheme http --ip 172.16.10.227 --port 51800" } } }
建立 StartupExtension.cs
我們需要在服務啟動後,將服務自動註冊到 Consul 的代理伺服器叢集中,為此,需要封裝一些簡單的註冊程式碼,以便複用
public static class StartupExtension
{
/// <summary>
/// 定義服務健康檢查的url地址
/// </summary>
public const string HEALTH_CHECK_URI = "/consul/health/check";
/// <summary>
/// 讀取 Consul 配置,注入服務
/// </summary>
/// <param name="service"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddConsulConfig(this IServiceCollection service,
IConfiguration configuration)
{
var clientConfig = configuration.GetSection("Consul").Get<ConsulConfig>();
service.Configure<ConsulConfig>(configuration.GetSection("Consul"));
return service;
}
/// <summary>
/// 將 ConsulClient 注入管道
/// </summary>
/// <param name="app"></param>
/// <param name="configuration"></param>
/// <param name="lifetime"></param>
/// <param name="cc"></param>
/// <returns></returns>
public static IApplicationBuilder UseConsul(this IApplicationBuilder app,
IConfiguration configuration,
IApplicationLifetime lifetime,
IOptions<ConsulConfig> cc)
{
var clientConfig = cc.Value;
//獲取服務執行偵聽的地址和埠作為健康檢查的地址
var clientIP = new Uri($"{configuration["scheme"]}://{configuration["ip"]}:{configuration["port"]}");
var serviceId = $"{clientConfig.ClientName}-{clientIP.Host}-{clientIP.Port}";
var ipv4 = clientIP.Host;
var consulClient = new ConsulClient(config =>
{
config.Address = new Uri(clientConfig.Server);
config.Datacenter = clientConfig.DataCenter;
});
var healthCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(7), // 服務啟動 7 秒後註冊服務
Interval = TimeSpan.FromSeconds(9), // 健康檢查的間隔時間為:9秒
HTTP = $"{clientIP.Scheme}://{ipv4}:{clientIP.Port}{HEALTH_CHECK_URI}"
};
var regInfo = new AgentServiceRegistration()
{
Checks = new[] { healthCheck },
Address = ipv4,
ID = serviceId,
Name = clientConfig.ClientName,
Port = clientIP.Port
};
consulClient.Agent.ServiceRegister(regInfo).GetAwaiter().GetResult();
lifetime.ApplicationStopped.Register(() =>
{
consulClient.Agent.ServiceRegister(regInfo);
});
return app;
}
/// <summary>
/// 實現健康檢查輸出,無需另行定義 Controller
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder MapHealthCheck(this IApplicationBuilder app)
{
app.Map(HEALTH_CHECK_URI, s =>
{
s.Run(async context =>
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("Health check {0}", DateTime.Now);
Console.ForegroundColor = ConsoleColor.Gray;
await context.Response.WriteAsync("ok");
});
});
return app;
}
}
上面的程式碼,實現是服務註冊和健康檢查的邏輯,程式碼比較簡單,每個方法頭部都有註釋,應該還是比較清晰,這裡就不再過多解釋了,接下來開始在 Startup.cs 中啟用 ConsulClient。
服務注入
public void ConfigureServices(IServiceCollection services)
{
services.AddConsulConfig(this.Configuration);
...
}
加入請求管道佇列
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
IApplicationLifetime lifetime,
IOptions<ConsulConfig> cc)
{
app.UseConsul(this.Configuration, lifetime, cc);
app.MapHealthCheck();
...
}
定義業務介面
下面簡單的實現一個 Controller,在該 Controller 裡面增加兩個業務介面,方便呼叫就好
[HttpGet("index")]
public ActionResult<string> Index()
{
return "Hello wrold";
}
[HttpGet("add/{x:int}/{y:int}")]
public ActionResult<int> Add(int x, int y)
{
var result = x + y;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("x+y={0}", result);
Console.ForegroundColor = ConsoleColor.Gray;
return result;
}
啟動服務,執行註冊
好了,到這裡,服務註冊的準備工作基本完成,接下來,按 F5 啟動程式,程式將自動進行服務註冊等工作
圖中藍色部分,就是 Consul 代理伺服器叢集對當前服務執行的健康檢查,健康檢查的原則只有一條,執行 http 請求,並返回 httpstatus=200 即視為健康,開啟 Consul 的 Web 控制檯介面,檢視實際的服務狀態
從上圖中可以看到,服務狀態是正常的(綠色)
服務發現
Consul 系統了許多 api 介面,供服務閘道器(或者代理)從 Consul 中獲取已註冊的健康的服務,比如下面的 api 地址
獲取所有已註冊的健康的服務
http://172.16.1.218:8500/v1/agent/services
獲取指定的服務
http://172.16.1.218:8500/v1/agent/service/node-1-172.16.10.227-51800
上圖中的內容,就是單個服務的註冊資訊,圖中紅色部分,是真實的服務的主機地址和偵聽的埠,閘道器代理可以將指定路由轉發到該地址實現業務呼叫。
結束語
截止目前為止,我們實現了部署 Consul 代理伺服器叢集、服務註冊、發現,但是目前來說,還沒有完全實現業務呼叫,現在,還缺少關鍵的一環:那就是服務閘道器;服務閘道器的呼叫,我們放在下一篇
原始碼下載
本示例所有程式碼都已託管到 Github,歡迎下載:https://github.com/lianggx/Examples/tree/master/Ron.Con