.Net Core微服務入門全紀錄(二)——Consul-服務註冊與發現(上)

前言

上一篇【.Net Core微服務入門全紀錄(一)——項目搭建】講到要作到服務的靈活伸縮,那麼須要有一種機制來實現它,這個機制就是服務註冊與發現。固然這也並非必要的,若是你的服務實例不多,而且很穩定,那麼就沒有必要使用服務註冊與發現。html

服務註冊與發現

  • 服務註冊:簡單理解,就是有一個註冊中心,咱們的每一個服務實例啓動時,都去註冊中心註冊一下,告訴註冊中心個人地址,端口等信息。一樣的服務實例要刪除時,去註冊中心刪除一下,註冊中心負責維護這些服務實例的信息。
  • 服務發現:既然註冊中心維護了各個服務實例的信息,那麼客戶端經過註冊中心就很容易發現服務的變化了。

有了服務註冊與發現,客戶端就不用再去配置各個服務實例的地址,改成從註冊中心統一獲取。
那註冊中心又是怎麼保證每一個地址的可用狀態呢,假如某個實例掛了怎麼辦呢?原則上掛掉的實例不該該被客戶端獲取到,因此就要提到:健康檢查 。git

  • 健康檢查:每一個服務都須要提供一個用於健康檢查的接口,該接口不具有業務功能。服務註冊時把這個接口的地址也告訴註冊中心,註冊中心會定時調用這個接口來檢測服務是否正常,若是不正常,則將它移除,這樣就保證了服務的可用性。

常見註冊中心有 Consul、ZooKeeper、etcd、Eureka。github

Consul

Consul官網:https://www.consul.io/
Consul的主要功能有服務註冊與發現、健康檢查、K-V存儲、多數據中心等。docker

  • Consul安裝:很簡單,直接在官網下載解壓便可。
  • Consul運行:在consul.exe目錄下打開命令行執行 consul.exe agent -dev
  • 瀏覽器訪問:http://localhost:8500/

    Consul已成功運行。

服務註冊

  • 首先Nuget安裝一下Consul:

    這個類庫裏封裝了Consul的api操做,方便咱們直接使用。固然本身去寫http調用Consul的接口也不是不行。。。接口說明:https://www.consul.io/api-docsjson

  • 改造一下訂單服務的代碼:
    api

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"], //服務IP
                Port = 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:app

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也會移除,有一點區別。

那麼註冊,發現,健康檢查功能都完成了,下一步就該考慮客戶端如何拿到這些服務實例的地址了。

代碼放在:https://github.com/xiajingren/NetCoreMicroserviceDemo

未完待續...

相關文章
相關標籤/搜索