• .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 (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

  • 相关阅读:
    C# 微信品牌会员卡开发(微信会员卡2.0)
    管理者问卷调查
    二:elementui源码解析之改造demoblock可以直接在卡片里编辑修改代码并生效渲染到界面上
    MySql 的@符号定义一个变量在sql里的占位符作用
    swift 代码段的重构
    k8skubeadm高可用安装部署
    LeetCode> 71. 简化路径
    Linux进程管理
    Linux中断下半部及推后执行的工作
    Linux进程调度
  • 原文地址:https://www.cnblogs.com/liyouming/p/13415171.html
Copyright © 2020-2023  润新知