.NetCore快速上手Consul,留給自己一點思考的空間

      互聯網熱潮下,「微服務」技術架構成為了一種高大上的技術,其顧名思義就是將傳統的大的業務服務拆分成獨立的小服務,當拆分的服務慢慢多起來的時候,我們會發現服務地址很難管理,傳統的方式一般會通過配置文件或者資料庫存儲,這種手動的維護的方式顯然不夠靈活,如果某個服務掛了,系統得不到及時的通知,只能等維護人員處理。這個時候如果有個中間件來做服務發現的話,顯然會顯得很靈活。

      .Net平台上使用「微服務」時做服務發現,目前來說Cosul是一個不錯的選擇。

什麼是Consul

Consul是一種提供了服務發現,配置管理等功能的服務網格解決方案,是一種分散式,高可用的服務管理架構。

主要功能:

服務發現(Service Discovery):Consul提供了通過DNS或者HTTP API介面的方式來發現服務。服務與服務之間通過Consul中心來統一獲取調用服務的地址,而不再是傳統模式下服務之間調用的「硬編碼」配置管理。

服務註冊(Service Register):Consul提供了兩種方式進行服務註冊。第一種通過Http API的方式在服務啟動時註冊,第二種通過json配置文件的方式註冊。

健康檢查(Health Checking):一旦服務註冊到Consul的client節點下,consul就會根據服務註冊時配置的間隔時間,主動請求服務,以獲取服務的健康狀態,如發現不健康的服務,則將該服務從consul中註銷。

Key/Value存儲:應用程式可以根據自己的需要使用Consul提供的Key/Value存儲。 Consul提供了簡單易用的HTTP介面,結合其他工具可以實現動態配置、功能標記、領袖選舉等等功能。

 

一張圖簡單了解下Consul

client節點

Client表示consul的client模式,即客戶端模式。

1)client節點負責轉發請求給server節點,本身不持久化任何資訊

2)提供服務健康檢查

3)client節點支援任意擴展,數量不受限制。

server 節點

Server表示consul的server模式,即服務端模式。

1)server節點接收client的轉發請求,並本地持久化這些資訊,遇到故障後方便排查。

2)consul集群中至少有一個server,官方建議server節點的個數3個或者5個比較合適(集群管理)。server的集群解決了單點問題,server間需要選舉一個ledger,由server-ledger負責server集群之間的資訊同步。

3)採用Raft一致性協議,保證server節點之間數據一致

consul節點間通訊

Consul使用GossIP協議管理成員關係、廣播消息到整個集群,包括兩個gossip pool(LAN pool和WAN pool),LAN pool是同一個數據中心內部通訊的,WAN pool是多個數據中心之間通訊的,LAN pool有多個,WAN pool只有一個。

 

通過上圖我們會發現,consul推薦是以集群的形式部署。但是很多人剛開始接觸學習的時候並沒有那麼多機器模擬(Docker模擬咱先不討論了),server單節點模式是否可行呢

答案當然是可行的,但弊端也顯而易見,首先肯定會有單點故障問題,其次如果註冊的服務太多,server需要不斷的向服務發送請求,進行健康檢查,顯然大量、高頻次的請求會極大影響consul節點。

如果只有一個server節點,那麼實際上它自身即充當了client又充當了server。本文我們就以server單節點的模式,以最簡單的應用和大家學習一下如何在.netcore中使用consul

準備環境.netcore3.1 、consul部署包

首先下載consul,官方地址://www.consul.io/downloads,解壓我們會發現就是一個exe程式,掌握一些常用的cmd命令即可。

本地環境consul啟動CMD命令:

首先cd到consul.exe所在目錄目錄

然後輸入consul agent -server -bootstrap-expect=1 -node=service1 -bind=127.0.0.1 -data-dir=./data -client=0.0.0.0 -ui -config-dir=./config

常用參數說明:

-server:表示consul代理模式,有兩個選擇-server和-client

-bootstrap-expect:在一個數據中心中期望提供的server節點數量,只有等到指定數量的server全部啟動後,才會啟動集群(自行選舉ledger)

-node:集群中節點名稱,同一集群中唯一,默認為主機名

-bind:綁定集群內部通訊的地址,表示該節點監聽的地址,這個地址必須是集群內部所有節點可達的。默認是0.0.0.0(將綁定機器得所有地址,同時把 ipv4地址告訴集群得其他人)

-client:綁定客戶端的ip地址,默認127.0.0.1,可綁定多個。0.0.0.0表示誰都可以訪問。

-data-dir:用於存放Agent狀態的目錄

-ui:啟用web ui

-config-dir:配置目錄,將載入目錄中的 .hcl 或 .json 格式配置。 注意子路徑不會載入

啟動consul後,我們可以通過//localhost:8500訪問consuld的UI頁面

HTTP方式註冊.netcore服務
新建API項目(服務A)

Nuget添加consul依賴包

添加Consul參數配置

{
  "ConsulConfig": {
    "ServiceName": "WebApi1",
    "ServiceIP": "localhost",
    "ServicePort": 6000,
    "HealthCheck": "/healthcheck",
    "ConsulAddress": "//localhost:8500"
  }
}

註冊服務

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();
            //添加consul服務註冊
            app.UseConsul();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

 consul服務註冊具體實現邏輯

    public static class ConsulExtensions
    {
        public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
        {
            //獲取consul配置實例
            var consulConfig = app.ApplicationServices.GetRequiredService<IOptions<ConsulOptions>>().Value;
            //獲取應用程式聲明周期事件
            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
            var consulClient = new ConsulClient(c =>
            {
                //consul服務註冊地址
                c.Address = new Uri(consulConfig.ConsulAddress);
            });

            //服務註冊配置
            var registration = new AgentServiceRegistration()
            {
                ID = Guid.NewGuid().ToString(),
                Name = consulConfig.ServiceName,//服務名稱
                Address = consulConfig.ServiceIP,//服務IP
                Port = consulConfig.ServicePort,//服務埠
                Check = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動後多久註冊服務
                    Interval = TimeSpan.FromSeconds(10),//健康檢查時間間隔
                    HTTP = $"//{consulConfig.ServiceIP}:{consulConfig.ServicePort}{consulConfig.HealthCheck}",//健康檢查地址
                    Timeout = TimeSpan.FromSeconds(5)//超時時間
                }
            };

            //服務註冊
            consulClient.Agent.ServiceRegister(registration).Wait();

            //應用程式結束時  取消註冊
            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).Wait();
            });

            return app;
        }
    }

     1)使用IOptions管理配置類ConsulOptions,將配置文件(比如appsettings.json)中的配置反序列化至配置類的實例中

   2)AgentServiceRegistration代理服務註冊類,實例化註冊服務配置。

 

健康檢查介面

    [Route("[controller]")]
    [ApiController]
    public class HealthCheckController : ControllerBase
    {
        /// <summary>
        /// 健康檢查介面
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public IActionResult Get() => Ok();
    }

 consul節點會根據配置的間隔時間進行定期請求改健康檢查介面,獲取服務健康狀態。

 

服務啟動
查看consul UI頁面我們會發現我們註冊的服務WebApi1資訊已顯示

 

那麼如何做到服務發現呢?

通常情況下,如果服務B調用服務A,需要在服務B中配置並指向服務A的地址,也就是上面說的「硬編碼」的配置方式,這種模式下,如果服務A部署了多個,那麼就需要管理多個地址,並且服務B本身無法知道某個服務A掛掉就放棄請求。

而consul為我們提供了便利,現在服務B不用關心服務A具體的服務地址以及服務A是否健康。服務B直接請求consul服務地址,告訴它我要訪問服務A,consul就會啟動服務發現機制,找到所有已註冊並且健康的服務A,然後根據選擇其中的一個返回給服務B,然後服務B根據服務A具體的地址發起請求。consul幫我們管理了服務地址和服務健康檢查。

下面我們就模擬一下consul的服務發現。

服務發現
新建API項目(服務B)

Nuget添加consul依賴包

模擬服務發現

        [HttpGet]
        [Route("/consul")]
        public void ReuestConsul()
        {
            //獲取Consul配置地址
            var consulAddress = _configuration["ConsulAddress"]?.ToString();
            using (var consulClient = new ConsulClient(x => x.Address = new Uri(consulAddress)))
            {
                //獲取根據服務註冊名稱webApi1  獲取consul註冊的服務
                var services = consulClient.Catalog.Service("WebApi1").Result.Response;
                if (services != null && services.Any())
                {
                    //模擬隨機一台進行請求,這裡只是測試,可以選擇合適的負載均衡框架
                    Random r = new Random();
                    int index = r.Next(services.Count());
                    var service = services.ElementAt(index);
                    using (HttpClient client = new HttpClient())
                    {
                        //請求服務WebApi1
                        var response = client.GetAsync($"//{service.ServiceAddress}:{service.ServicePort}/api/values").Result;
                        string result = response.Content.ReadAsStringAsync().Result;
                    }
                }
            }
        }

      1)ConsulClient實例化consul服務節點

      2)consulClient.Catalog.Service(“服務名稱”).Result.Response根據服務名稱,獲取conusl註冊的所有服務地址資訊(IP、埠)。

 

好了,本文主要是採用server單節點的模式,和大家介紹了一下consul的特性,以及結合.netcore做了服務註冊和模擬了服務發現。

我相信,大家帶著動手測試一遍,也會有所思考和收穫。

Tags: