AgileConfig 1.6.0 發布 – 支援服務註冊與發現

大家好,好久沒有輸出博文了,一是因為比較忙,另外一個原因是最近主要的精力是在給 AgileConfig 添加一個新的功能:服務註冊與發現。
先說說為什麼會添加這個功能。我自己的項目是用 Consul 來做為服務註冊發現組件的。自從我上線了 AgileConfig 做為配置中心後,我就很少去 Consul 觀察服務的在線狀態了,因為 AgileConfig 客戶端列表已經在一定程度上能代表服務的狀態了。服務註冊發現與配置中心其實本質上都是解決了一類問題,那就是配置的動態化,所以大家會看到業界著名的組件很多都是同時實現這2個功能的,如 Consul,Nacos 等。所以我想乾脆把這個功能給加上吧,這樣可以省去部署一個組件。
當然也有同學說我不務正業,不去好好搞配置中心去搞什麼服務註冊發現。但是我還是做了。。。
不過大家放心 AgileConfig 的主業還是在配置中心上,服務註冊發現只是附贈的小菜,可以用也可以不用,決定權完全在你。在實現上我也是對兩個功能是完全解耦的。也就是說這2個功能都是互不影響獨立運行的。唯一有交集的一個地方是,如果配置中心的客戶端的 websocket 通道建立成功的時候,服務的心跳會借用這個通道。
✨✨✨Github地址://github.com/dotnetcore/AgileConfig 開源不易,歡迎star✨✨✨

什麼是服務註冊與發現

首先先讓我們回顧下服務註冊發現的概念。
在實施微服務之後,我們的調用都變成了服務間的調用。服務間調用需要知道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": "//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個渠道:
  • websockt
    長連接,如果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地址://github.com/dotnetcore/AgileConfig 開源不易,歡迎star✨✨✨

演示地址://agileconfig_server.xbaby.xyz/ 超級管理員帳號:admin 密碼:123456

關注我的公眾號一起玩轉技術