天天看點

什麼是服務注冊與發現

大家好,好久沒有輸出博文了,一是因為比較忙,另外一個原因是最近主要的精力是在給 AgileConfig 添加一個新的功能:服務注冊與發現。

先說說為什麼會添加這個功能。我自己的項目是用 Consul 來做為服務注冊發現元件的。自從我上線了 AgileConfig 做為配置中心後,我就很少去 Consul 觀察服務的線上狀态了,因為 AgileConfig 用戶端清單已經在一定程度上能代表服務的狀态了。服務注冊發現與配置中心其實本質上都是解決了一類問題,那就是配置的動态化,是以大家會看到業界著名的元件很多都是同時實作這2個功能的,如 Consul,Nacos 等。是以我想幹脆把這個功能給加上吧,這樣可以省去部署一個元件。

當然也有同學說我不務正業,不去好好搞配置中心去搞什麼服務注冊發現。但是我還是做了。。。

不過大家放心 AgileConfig 的主業還是在配置中心上,服務注冊發現隻是附贈的小菜,可以用也可以不用,決定權完全在你。在實作上我也是對兩個功能是完全解耦的。也就是說這2個功能都是互不影響獨立運作的。唯一有交集的一個地方是,如果配置中心的用戶端的 websocket 通道建立成功的時候,服務的心跳會借用這個通道。

✨✨✨Github位址:https://github.com/dotnetcore/AgileConfig 開源不易,歡迎star✨✨✨

✨✨✨Gitee位址:https://gitee.com/kklldog/AgileConfig ✨✨✨

什麼是服務注冊與發現

首先先讓我們回顧下服務注冊發現的概念。

在實施微服務之後,我們的調用都變成了服務間的調用。服務間調用需要知道IP、端口等資訊。再沒有微服務之前,我們的調用資訊一般都是寫死在調用方的配置檔案裡(當然這話不絕對,有些公司會把這些資訊寫到資料庫等公共的地方,以友善維護)。又由于業務的複雜,每個服務可能依賴N個其他服務,如果某個服務的IP,端口等資訊發生變更,那麼所有依賴該服務的服務的配置檔案都要去修改,這樣顯然太麻煩了。有些服務為了負載是有個多個執行個體的,而且可能是随時會調整執行個體的數量。如果每次調整執行個體數量都要去修改其他服務的配置并重新開機那太麻煩了。

為了解決這個問題,業界就有了服務注冊發現元件。

假設我們有服務A需要調用服務B,并且有服務注冊發現元件R。整個大緻流程将變成大概3部:

服務B啟動向服務R注冊自己的資訊

服務A從服務R拉取服務B的資訊

服務A調用服務B

有了服務注冊發現元件之後,當修改A服務資訊的時候再也不用去修改其他相關服務了。

什麼是服務注冊與發現

參考我的另外一篇:.Net Core with 微服務 - Consul 注冊中心

使用服務注冊與發現

使用服務注冊與發現功能需要更新服務端與用戶端至 1.6.0 及以上版本。

啟動服務端

服務端更新至 latest 鏡像或 v-1.6.0 以上的鏡像。

使用 docker 運作服務端執行個體:

sudo docker run \
--name agile_config \
-e TZ=Asia/Shanghai \
-e adminConsole=true \
-e db:provider=sqlite \
-e db:conn="Data Source=agile_config.db" \
-p 5000:5000 \
#-v /your_host_dir:/app/db \
-d kklldog/agile_config:latest           

複制

基本的使用沒有太大的變化,隻是在界面上添加了服務的相關管理界面,這裡不在贅述。

相關教程: .Net Core & Agile Config配置中心

使用用戶端

用戶端需要從 nuget 上安裝 1.6.0 版本以上的 client 包。

Install-Package AgileConfig.Client -Version 1.6.0           

複制

新版的 client 簡化了使用方式,以下以 .net6 為示例:

調用 UseAgileConfig 擴充方法即可注入 AgileConfig client .

var builder = WebApplication.CreateBuilder(args);

//use agileconfig client
builder.Host.UseAgileConfig();

...           

複制

在 appsettings.json 添加配置資訊:

"AgileConfig": {
    "appId": "test_app",
    "secret": "test_app",
    "nodes": "http://agileconfig_server.xbaby.xyz/",
    "name": "client123",
    "tag": "tag123",

    "serviceRegister": { //服務注冊資訊,如果不配置該節點,則不會啟動任何跟服務注冊相關的服務 可選
      "serviceId": "net6", //服務id,全局唯一,用來唯一标示某個服務
      "serviceName": "net6MVC服務測試", //服務名,可以重複,某個服務多執行個體部署的時候這個serviceName就可以重複
      "ip": "127.0.0.1", //服務的ip 可選
      "port": 5005, //服務的端口 可選
  }           

複制

其中 appId , secret 等配置同原來配置中心的使用方式沒有任何改變。

serviceRegister

節點描述的是服務注冊資訊(如果删除這個節點那麼服務注冊功能就不會啟動):

  • serviceId

    服務id,全局唯一,用來唯一标示某個服務

  • serviceName

    服務名,可以重複,某個服務多執行個體部署的時候這個serviceName就可以重複

  • ip

    服務的ip 可選

  • port

    服務的端口 可選

  • metaData

    一個字元串數組,可以攜帶一些服務的相關資訊,如版本等 可選

  • alarmUrl

    告警位址 可選。

    如果某個服務出現異常情況,如一段時間内沒有心跳,那麼服務端會往這個位址 POST 一個請求并且攜帶服務相關資訊,使用者可以自己去實作提醒功能,比如發短信,發郵件等:

{
    "serviceId":"0001",
    "serviceName":"xxxx",
    "time":"2022-01-01T12:00:000",
    "status":"Unhealty",
    "message": "服務不健康"
}           

複制

  • heartbeat:mode

    指定心跳的模式,server/client 。server代表服務端主動檢測,client代表用戶端主動上報。不填預設client模式 可選

  • heartbeat:interval

    心跳的間隔,預設時間30s 可選

  • heartbeat:url

    心跳模式為 server 的時候需要填寫健康檢測位址,如果是httpstatus為200段則判定存活,其它都視為失敗 可選

服務的注冊

當配置好用戶端後,啟動對應的應用程式,服務資訊會自動注冊到服務端并且開始心跳。如果服務正确注冊到服務端,控制台的服務管理界面可以檢視:

什麼是服務注冊與發現

服務發現

現在服務已經注冊上去了,那麼怎麼才能拿到注冊中心所有的服務呢?同樣非常簡單,在程式内隻要注入

IDiscoveryService

接口就可以通過它拿到所有的注冊的服務。

public interface IDiscoveryService
    {
        string DataVersion { get; }
        List<ServiceInfo> UnHealthyServices { get; }
        List<ServiceInfo> HealthyServices { get; }
        List<ServiceInfo> Services { get; }
        Task RefreshAsync();
    }           

複制

除了接口内置的方法,還有幾個擴充方法友善使用者使用,比如随機一個服務:

public static class DiscoveryServiceExtension
    {
        public static IEnumerable<ServiceInfo> GetByServiceName(this IDiscoveryService ds, string serviceName)
        {
            return ds.Services.GetByServiceName(serviceName);
        }

        public static ServiceInfo GetByServiceId(this IDiscoveryService ds, string serviceId)
        {
            return ds.Services.GetByServiceId(serviceId);
        }

        public static ServiceInfo RandomOne(this IDiscoveryService ds, string serviceName)
        {
            return ds.Services.RandomOne(serviceName);
        }
    }           

複制

至此服務的注冊與發現就已經完成了。

一些重要的資訊

以上就是服務注冊發現的簡單使用,但是還有一些比較重要的資訊希望大家在使用之前能夠了解,這樣有利于更好的使用以及出現問題的時候定位問題。

高可用

同 AgileConfig 的配置中心功能一樣,服務注冊後最後都是寫到了資料庫裡。AgileConfig 的服務端可以部署多個來防止單點故障,同時可以分擔壓力。是以高可用的最佳實踐就是部署 2 個以上的服務端節點,然後資料庫做高可用方案。這樣足夠應付大多數要求不是特别高的場景。

強一緻性

同上 AgileConfig 通過資料庫保證多個節點部署的時候的一緻性問題。

服務的健康檢測

服務的健康檢測一般有2種方案:

  1. 服務端主動詢問
  2. 用戶端主動心跳

    AgileConfig 同時支援以上2個方案。AgileConfig client 預設實作了主動心跳。AgileConfig client 的主動心跳有2個管道:

  • websocket

    長連接配接,如果AgileConfig client做為配置中心用戶端是正常工作的,那麼心跳會走websocket通道

  • http

    如果 websocket 不可用,那麼會直接發起 http 請求做為心跳。

    但是對于一些應用主動的心跳并不能代表服務真的是可以用的,因為心跳從服務已啟動就會開始,但是某些接口可能還沒真正的做好準備被調用。那麼這個時候就可以選擇服務端主動詢問(heartbeat:mode=server)對應的檢測接口來确定服務是否真的可用。

    AgileConfig 其實還實作了第三種方式:

  1. 不檢測

    如果一個服務你确定它會永遠線上,或者是沒辦法內建 AgileConfig client 的 sdk ,那麼你可以标記它為不檢測,這樣它會一直是健康狀态。

服務發現是如何即時更新的

我們的 client 在啟動後會拉取一次全量的服務清單。但是服務是會不斷的上線,下線的,是以服務狀态的更新是需要通知用戶端的,然後用戶端去拉取新的服務清單。AgileConfig 同樣有2個政策來保證服務清單的即時重新整理:

  1. 當服務狀态變化的時候,服務端通過 websocket 即時通知所有的 client 主動重新整理配置清單
  2. 如果服務端的主動通知由于網絡等原因失效的時候,client 會在每次心跳的時候比較本地服務清單 md5 版本跟服務端的清單的 md5 資訊,如果不一緻,那麼 client 會主動拉取一次新的服務清單。

關閉服務注冊與發現

删除

serviceRegister

配置節點或不要配置任何資訊。

最後

✨✨✨Github位址:https://github.com/dotnetcore/AgileConfig 開源不易,歡迎star✨✨✨

✨✨✨Gitee位址:https://gitee.com/kklldog/AgileConfig ✨✨✨

示範位址:http://agileconfig_server.xbaby.xyz/ 超級管理者賬号:admin 密碼:123456