.NetCore下構建自己的服務配置中心-手動造輪子


本人主要利用IdentityServer4以及SignalR來實現,IdentityServer4作為認證,SignalR來交互配置,這里一些代碼可能就是部分提出來,主要介紹實現原理及方法

實現配置中心核心的兩個點我們要放在

1、配置文件如何傳送

2、配置文件如何動態的更新

配置文件的傳送結合SignalR來實現

思考:什么樣的客戶端可以來獲取配置?

這里客戶端我們配置了

這里我直接結合Identityserver4,配置客戶端id,客戶端密鑰,配置中心地址、在配置一個IdentityServer4授權地址, 根據這些配置設計下配置中心的 數據庫表,這里直接略

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConfigCenter": {
    "AuthUrl": "http://192.168.0.42:7000",
    "ClientId": "configclient",
    "ClientSecret": "configclient",
    "ServerUrl": "http://localhost:6002"
  },

  "AllowedHosts": "*"
}

然后只有手魯SignalR代碼來出來連接消息交互了,不用在去早socket的輪子了,此處了略去,為了准備給自己的服務使用,只有把這個寫成SDK

這里我們需要一個服務類:ClientServices 這個類用來出來 SignalR相關處理 以及配置Json數據的格式話,需要json配置轉換成 .NetCore配置 ,開過配置中的 Data對象的都知道 這個Data中的格式是 {Logging:LogLevel,"1231"}這種鍵值對,怎么來把Json數據處理成為這個數據呢?后面介紹簡便方法

這里對象類需要了解清楚 IConfigruationBuilder、IConfigurationProvider、IConfigurationSource ,這塊不做介紹,而我們要做的就是結合這個來處理我們的配置

 

 建立了三個項目來處理

ConfigCenter:配置服務端API接口,提供給UI 配置管理相關接口

ConfigCenterClient :客戶端配置SDK,提供給服務端連接配置中心提供配置庫

ConfigCenterTest::客戶端服務測試API

配置文件動態更新

首先來開下ConfigurationProvider的源碼

  //
    // 摘要:
    //     Base helper class for implementing an Microsoft.Extensions.Configuration.IConfigurationProvider
    public abstract class ConfigurationProvider : IConfigurationProvider
    {
        //
        // 摘要:
        //     Initializes a new Microsoft.Extensions.Configuration.IConfigurationProvider
        protected ConfigurationProvider();

        //
        // 摘要:
        //     The configuration key value pairs for this provider.
        protected IDictionary<string, string> Data { get; set; }

        //
        // 摘要:
        //     Returns the list of keys that this provider has.
        //
        // 參數:
        //   earlierKeys:
        //     The earlier keys that other providers contain.
        //
        //   parentPath:
        //     The path for the parent IConfiguration.
        //
        // 返回結果:
        //     The list of keys for this provider.
        public virtual IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
        //
        // 摘要:
        //     Returns a Microsoft.Extensions.Primitives.IChangeToken that can be used to listen
        //     when this provider is reloaded.
        //
        // 返回結果:
        //     The Microsoft.Extensions.Primitives.IChangeToken.
        public IChangeToken GetReloadToken();
        //
        // 摘要:
        //     Loads (or reloads) the data for this provider.
        public virtual void Load();
        //
        // 摘要:
        //     Sets a value for a given key.
        //
        // 參數:
        //   key:
        //     The configuration key to set.
        //
        //   value:
        //     The value to set.
        public virtual void Set(string key, string value);
        //
        // 摘要:
        //     Generates a string representing this provider name and relevant details.
        //
        // 返回結果:
        //     The configuration name.
        public override string ToString();
        //
        // 摘要:
        //     Attempts to find a value with the given key, returns true if one is found, false
        //     otherwise.
        //
        // 參數:
        //   key:
        //     The key to lookup.
        //
        //   value:
        //     The value found at key if one is found.
        //
        // 返回結果:
        //     True if key has a value, false otherwise.
        public virtual bool TryGet(string key, out string value);
        //
        // 摘要:
        //     Triggers the reload change token and creates a new one.
        protected void OnReload();

注意可以看到 Load方法都是虛方法,就是提供給我擴展的,接下來就是關鍵的一步在這個上面處理

構建自己的服務提供類來重寫該方法,然后注入一個事件,提供給外部改變更新的能力

public class LYMConfigProvider : ConfigurationProvider
    {
        private ClientServices Client { get; }

        public LYMConfigProvider(ClientServices clientServices)
        {
            Client = clientServices;
        }

        private void LoadData(IDictionary<string, string> values)
        {
            foreach (var item in values)
            {

               if (item.Key.EndsWith("0"))
                {
                   var likekey=  item.Key.Substring(0, item.Key.Length - 1); foreach (var da in Data) { if (da.Key.StartsWith(likekey)) { Data.Remove(da.Key); } } } if (Data.ContainsKey(item.Key)) Data.Remove(item.Key); Data.Add(item.Key, item.Value);
 } } public override void Load() { Client.Changed += (arg) => { LoadData(arg.customdata); base.OnReload(); }; var lst = Client.InitClientConfig(); LoadData(lst); } }

通過注冊改變事件,外部就可以通過該事件來實現配置的更新了,值得注意的Load的時候我們需要初始化下配置,畢竟SignalR連接回復初始配置會出現延遲,導致Startup中IConfiguraion初始配置為空

接下來說一個關鍵的問題,就是得到的Json怎么序列化出來到Data中,而Data的類型又是:

//
        // 摘要:
        //     The configuration key value pairs for this provider.
        protected IDictionary<string, string> Data { get; set; }

不怕麻煩的朋友可以自己寫遞歸各種轉換,但是那樣太麻煩了,只能用絕招了,通過ConfigurationBuilder對象Build出來的配置提供類都有一個Data,這個Data .NetCode已經幫我們出來好了,於是自己去構建了一個ConfigurationBuilder這樣來處理看行不行?

  byte[] by = Encoding.UTF8.GetBytes(json);
            MemoryStream ms = new MemoryStream(by);
   var builder = new ConfigurationBuilder().AddJsonStream(ms).Build();

這里調試監控你可以看到Data是有值的,但是你就是取不到,會出現類型轉換錯誤,就算得到Providers強轉自己的類型還是會報錯,且Data在ConfigurationProvider中是protect ,所以為了處理這個問題,我們不得不建立另外的自己的類來擴展下,構建自定義的配置資源以及配置提供類

因為這里是JsonStream,所以我們來擴展JsonStream就ok,這里我們提供了一個公共的GetData方法,來獲取基類中的Data

 public class CustomJsonConfigrationProvider : JsonStreamConfigurationProvider
    {
        public CustomJsonConfigrationProvider(JsonStreamConfigurationSource jsonStreamConfigurationSource) : base(jsonStreamConfigurationSource)
        { 
        
        }
        public IDictionary<string, string> GetData()
        {
            return base.Data;
        }
    }
public class CustomJsonConfigrationSource: JsonStreamConfigurationSource
    {
        public override IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            return new CustomJsonConfigrationProvider(this);
        }
    }

 

所以如何把Json裝換成.netcore中的配置,我們只需要如下代碼即可:

    public IDictionary<string, string> BuildDictionary(string json)
        {
            byte[] by = Encoding.UTF8.GetBytes(json);
            MemoryStream ms = new MemoryStream(by);
            var source = new CustomJsonConfigrationSource();
            source.Stream = ms;
            var builder = new ConfigurationBuilder().Add(source).Build();
           var data = builder.Providers.First() as CustomJsonConfigrationProvider;
            return data.GetData();
        }

接下來是校驗成果的時候了,通過引用客戶端SDK,並配置上自己的配置,准備下服務端的初始配置

 public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .ConfigLYMConfiguration() //這里就是我們SDK自己寫的配置擴展方法
           .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

      
    }

這里服務端准備一段配置:{"data":"this the init configuration data"} 以及測試發送配置接口

 

 

 

 下面我們通過服務的配置來修改下在刷新頁面:

 

 

 

 

 

 

 

 在環境配置中添加2兩個配置

 

 

 

 

 接下來我們用測試客戶端啟動起來看下,可以看到客戶端實例的信息,以及使用的默認配置

 

 並且訪問下客戶端站點

 

 下面我們服務端啟用Development配置然后刷新客戶端看結果:

 

 

實現了針對通過一個客戶端分布式部署統一更新配置,以及獲取連接實例,針對指定服務實例更新配置的功能

 

 

參考文獻:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#custom-configuration-provider

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM