系列文章目錄
1、.Net Core微服務入門系列(一)——項目搭建
2、.Net Core微服務入門全紀錄(二)——Consul-服務注冊與發現(上)
3、.Net Core微服務入門全紀錄(三)——Consul-服務注冊與發現(下)
4、.Net Core微服務入門全紀錄(四)——Ocelot-API網關(上)
5、.Net Core微服務入門全紀錄(五)——Ocelot-API網關(下)
6、.Net Core微服務入門全紀錄(六)——EventBus-事件總線
7、.Net Core微服務入門全紀錄(七)——IdentityServer4-授權認證
8、.Net Core微服務入門全紀錄(八)——Docker Compose與容器網絡
前言📃
關于 微服務 的概念解釋網上有很多, 個人理解微服務是一種系統架構模式,它和語言無關,和框架無關,和工具無關,和服務器環境無關。
微服務思想 是將傳統的單體系統按照業務拆分成多個職責單一、且可獨立運行的接口服務。至于服務如何拆分,沒有明確的定義。幾乎任何后端語言都能做微服務開發。微服務也并不是完美無缺的,微服務架構會帶來更多的問題,增加系統的復雜度,引入更多的技術棧。
上一篇【.Net Core微服務入門全紀錄(一)——項目搭建】講到要做到服務的靈活伸縮,那么需要有一種機制來實現它,這個機制就是服務注冊與發現。當然這也并不是必要的,如果你的服務實例很少,并且很穩定,那么就沒有必要使用服務注冊與發現。
一、服務注冊與發現
-
服務注冊:簡單理解,就是有一個注冊中心,我們的每個服務實例啟動時,都去注冊中心注冊一下,告訴注冊中心我的地址,端口等信息。同樣的服務實例要刪除時,去注冊中心刪除一下,注冊中心負責維護這些服務實例的信息。
-
服務發現:既然注冊中心維護了各個服務實例的信息,那么客戶端通過注冊中心就很容易發現服務的變化了。
有了服務注冊與發現,客戶端就不用再去配置各個服務實例的地址,改為從注冊中心統一獲取。
那注冊中心又是怎么保證每個地址的可用狀態呢,假如某個實例掛了怎么辦呢?原則上掛掉的實例不應該被客戶端獲取到,所以就要提到:健康檢查 。
🎯健康檢查:每個服務都需要提供一個用于健康檢查的接口,該接口不具備業務功能。服務注冊時把這個接口的地址也告訴注冊中心,注冊中心會定時調用這個接口來檢測服務是否正常,如果不正常,則將它移除,這樣就保證了服務的可用性。
常見注冊中心有 Consul
、ZooKeeper
、etcd
、Eureka
。
二、Consul
Consul 官網:https://www.consul.io/
Consul 的主要功能有服務注冊與發現、健康檢查、K-V存儲、多數據中心等。
- Consul安裝:很簡單,直接在官網下載解壓即可。
- Consul運行:在
consul.exe
目錄下打開命令行執行consul.exe agent -dev
- 瀏覽器訪問:http://localhost:8500/
Consul
已成功運行。
三、服務注冊
首先 Nuget
安裝一下 Consul
:
這個類庫里封裝了Consul的api操作,方便我們直接使用。當然自己去寫http調用Consul的接口也不是不行。。。接口說明:https://www.consul.io/api-docs
改造一下訂單服務的代碼:
ConsulHelper.cs:
public static class ConsulHelper{/// <summary>/// 服務注冊到consul/// </summary>/// <param name="app"></param>/// <param name="lifetime"></param>public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IConfiguration configuration, IHostApplicationLifetime lifetime) {var consulClient = new ConsulClient(c =>{//consul地址c.Address = new Uri(configuration["ConsulSetting:ConsulAddress"]);});var registration = new AgentServiceRegistration(){ID = Guid.NewGuid().ToString(),//服務實例唯一標識Name = configuration["ConsulSetting:ServiceName"],//服務名Address = configuration["ConsulSetting:ServiceIP"], //服務IPPort = int.Parse(configuration["ConsulSetting:ServicePort"]),//服務端口 因為要運行多個實例,端口不能在appsettings.json里配置,在docker容器運行時傳入Check = new AgentServiceCheck(){DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久后注冊Interval = TimeSpan.FromSeconds(10),//健康檢查時間間隔HTTP = $"http://{configuration["ConsulSetting:ServiceIP"]}:{configuration["ConsulSetting:ServicePort"]}{configuration["ConsulSetting:ServiceHealthCheck"]}",//健康檢查地址Timeout = TimeSpan.FromSeconds(5)//超時時間}};//服務注冊consulClient.Agent.ServiceRegister(registration).Wait();//應用程序終止時,取消注冊lifetime.ApplicationStopping.Register(() =>{consulClient.Agent.ServiceDeregister(registration.ID).Wait();});return app;}}
appsettings.json:
{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","ConsulSetting": {"ServiceName": "OrderService","ServiceIP": "localhost","ServiceHealthCheck": "/healthcheck","ConsulAddress": "http://host.docker.internal:8500"//注意,docker容器內部無法使用localhost訪問宿主機器,如果是控制臺啟動的話就用localhost}
}
Startup.cs:
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.AddControllers();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});//服務注冊app.RegisterConsul(Configuration, lifetime);}}
OrdersController.cs:
[Route("[controller]")][ApiController]public class OrdersController : ControllerBase{private readonly ILogger<OrdersController> _logger;private readonly IConfiguration _configuration;public OrdersController(ILogger<OrdersController> logger, IConfiguration configuration){_logger = logger;_configuration = configuration;}[HttpGet]public IActionResult Get(){string result = $"【訂單服務】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}——" +$"{Request.HttpContext.Connection.LocalIpAddress}:{_configuration["ConsulSetting:ServicePort"]}";return Ok(result);}}
HealthCheckController.cs:
[Route("[controller]")][ApiController]public class HealthCheckController : ControllerBase{/// <summary>/// 健康檢查接口/// </summary>/// <returns></returns>[HttpGet]public IActionResult Get(){return Ok();}}
至此就完成了服務注冊,取消注冊,健康檢查等功能的代碼編寫。
同樣的改造一下產品服務,代碼差不多一樣,就不貼了。
四、運行服務
繼續在 docker
中運行服務實例,不習慣 docker
的話用控制臺啟動也行。
--ConsulSetting:ServicePort
參數就是傳入容器的端口信息。
docker build -t orderapi:1.0 -f ./Order.API/Dockerfile .
docker run -d -p 9060:80 --name orderservice orderapi:1.0 --ConsulSetting:ServicePort="9060"
docker run -d -p 9061:80 --name orderservice1 orderapi:1.0 --ConsulSetting:ServicePort="9061"
docker run -d -p 9062:80 --name orderservice2 orderapi:1.0 --ConsulSetting:ServicePort="9062"docker build -t productapi:1.0 -f ./Product.API/Dockerfile .
docker run -d -p 9050:80 --name productservice productapi:1.0 --ConsulSetting:ServicePort="9050"
docker run -d -p 9051:80 --name productservice1 productapi:1.0 --ConsulSetting:ServicePort="9051"
docker run -d -p 9052:80 --name productservice2 productapi:1.0 --ConsulSetting:ServicePort="9052"
至此,6個服務器實例都已運行,并且成功注冊到 Consul
。
隨便停止2個服務:
可以看到停止的服務已經在 Consul
中被移除。注意,這個是我們停止程序時主動調用 Consul
移除的。
//應用程序終止時,取消注冊
lifetime.ApplicationStopping.Register(() =>
{consulClient.Agent.ServiceDeregister(registration.ID).Wait();
});
當然程序發生異常,健康檢查不能正確響應的話,Consul
也會移除,有一點區別。
那么注冊,發現,健康檢查功能都完成了,下一步就該考慮客戶端如何拿到這些服務實例的地址了。