博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【NET CORE微服务一条龙应用】第二章 配置中心使用
阅读量:6234 次
发布时间:2019-06-21

本文共 8850 字,大约阅读时间需要 29 分钟。

背景

系列目录:

在分布式或者微服务系统里,通过配置文件来管理配置内容,是一件比较令人痛苦的事情,再谨慎也有湿鞋的时候,这就是在项目架构发展的过程中,配置中心存在的意义。

其实配置中心的组件已经有非常出名的案例,比如携程的阿波罗配置中心()

为什么又造轮子,因为不想发布项目的时候到处切管理平台。

基本要求

作为一个通用的配置组件,需要支持如下功能:

1、客户端定时刷新获信最新配置信息并进行热更新

2、配置有更新服务端主动推送重载或更新命令至客户端进行配置获取

所以涉及相对应组件如下:

1、支持广播的消息通知组件,目前使用redis(StackExchange.Redis)、Zookeeper(Rabbit.Zookeeper)实现客户端全局监听服务,服务端可以推送不同组建不同的命令

2、支持定时获取最新配置,目前使用HostedService实现全局统一启动,客户端实现全局启动接口,接口使用Timer进行定时获取配置

3、支持net core原生IConfiguration接口获取配置中心数据

 服务端设计

 

管理服务端主要实现:

1、三表增删改查

2、配置内容表,每次新增或者修改,当前配置信息版本号为,所以配置最大版本号然后加一

3、应用表列表增加主动通知功能

配置查询服务端

主要提供配置信息的查询接口

1、接口入参如下

public class QueryConfigInput    {        [NotEmpty("config_001","AppId不能为空")]        public string AppId { set; get; }        public long Version { set; get; }        [NotEmpty("config_002", "签名不能为空")]        public string Sign { set; get; }        [NotEmpty("config_005", "NamespaceName不能为空")]        public string NamespaceName { set; get; }        public string Env { set; get; }    }

2、查询逻辑

   2.1 入参基本验证

   2.2 AppId 密钥进行签名验证

   2.3 请求配置环境定位

   2.4 查询当前请求应用和共有配置应用

   2.5 查询大于当前查询版本号的配置信息并返回

配置中心客户端

客户端主要实现原理和功能

1、配置信息请求,当前Http请求,需根据配置信息组合请求url,然后请求获取配置,每次请求带上当前配置最大版本号(在以后请求时只获取有更新的配置)

2、配置信息本地存储(容灾),第一次获取成功后,把配置信息进行版本文件存储,以后的请求中当有配置更新时再进行文件存储。

3、当配置请求失败时进行本地文件配置信息的还原应用。

4、配置定时获取

5、客户端接收更新或者重载命令

6、原生IConfiguration配置查询支持

部分功能介绍

客户端参数

"ConfigServer": {    "AppId": "PinzhiGO",    "AppSercet": "xxxxxxxxxxxxx",    "ServerUrl": "http://10.10.188.136:18081/", // 配置查询服务端地址    "NamespaceName": "Pinzhi.Identity.WebApi",    "Env": "dev",    "RefreshInteval": 300  },

原生IConfiguration配置查询

查看AddJsonFile源码,可以发现实现自定义配置源,需要集成和实现ConfigurationProvider和IConfigurationSource两个方法

代码如下

public class BucketConfigurationProvider : ConfigurationProvider, IDataChangeListener, IConfigurationSource    {        private readonly ConfigurationHelper _configurationHelper;        public BucketConfigurationProvider(BucketConfigOptions options)        {            _configurationHelper = new ConfigurationHelper(options);            Data = new ConcurrentDictionary
(); } public override void Load() { DataChangeListenerDictionary.Add(this); Data = _configurationHelper.Get().ConfigureAwait(false).GetAwaiter().GetResult(); } private void SetData(ConcurrentDictionary
changeData) { foreach(var dic in changeData) { if (Data.ContainsKey(dic.Key)) Data[dic.Key] = dic.Value; else Data.Add(dic); } // Data = new Dictionary
(_configRepository.Data, StringComparer.OrdinalIgnoreCase); } public void OnDataChange(ConcurrentDictionary
changeData) { SetData(changeData); OnReload(); } public IConfigurationProvider Build(IConfigurationBuilder builder) => this; }

当有配置更新时,我们需要更新到ConfigurationProvider的Data中,所以我们需要实现自定义接口IDataChangeListener的OnDataChange方法,当客户端请求发现有配置更新时,会调用接口的OnDataChange把最新的配置信息传递进来。

启用原生IConfiguration方法如下:

.ConfigureAppConfiguration((hostingContext, _config) =>                   {                       _config                       .SetBasePath(Directory.GetCurrentDirectory())                       .AddJsonFile("appsettings.json", true, true)                       .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)                       .AddEnvironmentVariables(); // 添加环境变量                       var option = new BucketConfigOptions();                       _config.Build().GetSection("ConfigServer").Bind(option);                       _config.AddBucketConfig(option);                   })

定时配置获取

常规做法是写一个hostedservice的方法,然后写一个timer去定时获取,由于其他的组件可能都需要有定时的情况,我们统一处理了一下定时的任务,每个组件实现IExecutionService接口,然后组件会在启动的时候循环调用IExecutionService的StartAsync的方法,组件包Bucket.Config.HostedService,原理比较简单,使用代码如下:

// 添加全局定时任务            services.AddBucketHostedService(builder => {                builder.AddAuthorize().AddConfig().AddErrorCode();            });
public class AspNetCoreHostedService : IBucketAgentStartup    {        private readonly IEnumerable
_services; public AspNetCoreHostedService(IEnumerable
services) { _services = services; } public async Task StartAsync(CancellationToken cancellationToken = default(CancellationToken)) { foreach (var service in _services) await service.StartAsync(cancellationToken); } public async Task StopAsync(CancellationToken cancellationToken = default(CancellationToken)) { foreach (var service in _services) await service.StopAsync(cancellationToken); } }

组件命令监听

和上面原则一样,也进行了统一的封装,目前监听主要实现了redis和zookeeper,下面举例redis

组件监听需实现接口

public interface IBucketListener    {        string ListenerName { get; }        Task ExecuteAsync(string commandText);    }

命令序列化实体

public class NetworkCommand    {        public string NotifyComponent { set; get; }        public string CommandText { set; get; }    }    public enum NetworkCommandType    {        ///         /// 更新        ///         Refresh,        ///         /// 重载        ///         Reload,    }

在hostedservice启动时实现

public Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))        {            _subscriber = _redisClient.GetSubscriber(_redisListenerOptions.ConnectionString);            return _subscriber.SubscribeAsync(RedisListenerKey, (channel, message) =>            {                var command = JsonConvert.DeserializeObject
(message); _extractCommand.ExtractCommandMessage(command); }); }

在接口IExtractCommand里会根据各个监听组件的ListenerName进行对应的调用

使用方法如下:

// 添加应用监听            services.AddListener(builder => {                //builder.UseRedis();                builder.UseZookeeper();                builder.AddAuthorize().AddConfig().AddErrorCode();            });

所以对应组件实现的命令监听只要关心自身逻辑即可吗,代码如下

public class BucketConfigListener : IBucketListener    {        public string ListenerName => "Bucket.Config";        private readonly IDataRepository _dataRepository;        public BucketConfigListener(IDataRepository dataRepository)        {            _dataRepository = dataRepository;        }        public async Task ExecuteAsync(string commandText)        {            if (!string.IsNullOrWhiteSpace(commandText) && commandText == NetworkCommandType.Refresh.ToString())                await _dataRepository.Get();            if (!string.IsNullOrWhiteSpace(commandText) && commandText == NetworkCommandType.Reload.ToString())                await _dataRepository.Get(true);        }    }

配置中心使用配置如下

.ConfigureAppConfiguration((hostingContext, _config) =>                   {                       _config                       .SetBasePath(Directory.GetCurrentDirectory())                       .AddJsonFile("appsettings.json", true, true)                       .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)                       .AddEnvironmentVariables(); // 添加环境变量                       var option = new BucketConfigOptions();                       _config.Build().GetSection("ConfigServer").Bind(option);                       _config.AddBucketConfig(option);                   })// ConfigureServices // 添加配置服务            services.AddConfigServer(Configuration);// 添加应用监听            services.AddListener(builder => {                //builder.UseRedis();                builder.UseZookeeper();                builder.AddAuthorize().AddConfig().AddErrorCode();            });            // 添加全局定时任务            services.AddBucketHostedService(builder => {                builder.AddAuthorize().AddConfig().AddErrorCode();            });//使用private readonly IConfiguration _configuration;        private readonly IConfig _config;        public AuthController(IConfiguration configuration, IConfig config)        {            _configuration= configuration;            _config= config;        }// 获取值_configuration.GetValue
("qqqq");_config.StringGet("qqqq");

Appsettings.json相关配置信息转移至配置中心

由于配置中心客户端实现了原生的IConfiguration,所以appsetting的相关配置我们完全可以移至配置中心中,由于appsetting使用的是json,所以在配置中心服务端配置信息的Key需要转换,举例:

"BucketListener": {    "Redis": {      "ConnectionString": "127.0.0.1:6379,allowadmin=true",      "ListenerKey": "Bucket.Sample"    },    "Zookeeper": {      "ConnectionString": "localhost:2181",      "ListenerKey": "Bucket.Sample"    }  }

在配置中心key如下:

BucketListener:Redis:ConnectionString

BucketListener:Redis:ListenerKey

......

数组使用如下:

DbConfig:0:Name

DbConfig:0:DbType

DbConfig:1:Name

DbConfig:1:DbType

总结

个人写作水平有限,涉及的东西也很多,篇幅有限所以只做了大体介绍,忘谅解

本章涉及源码

 客户端组件

 配置查询服务端

 综合管理服务接口

转载于:https://www.cnblogs.com/tianxiangzhe/p/10342428.html

你可能感兴趣的文章
回收站(recyclebin)引发row cache lock
查看>>
nagios安装配置pnp4nagios-0.6
查看>>
VMware vSphere 5.1 群集深入解析(七)
查看>>
SQL Server 黑盒跟踪 -- 进一步了解sqldiag
查看>>
banner和背景的说明
查看>>
redhat6 + 11G RAC 双节点部署
查看>>
使用Handy Backup 6.2进行数据备份与还原(多图)
查看>>
计算机高手也不能编出俄罗斯方块——计算机达人成长之路(16)
查看>>
AD RMS保护电子邮件安全
查看>>
【COCOS2DX-LUA 脚本开发之八】使用Lua实现Http交互
查看>>
Discuz!NT负载均衡方案
查看>>
阿里巴巴搜索混部解密
查看>>
[转载]10步创建成功的Web2.0公司
查看>>
无线时代来临,谁来管理我的无线AP?
查看>>
无线路由Buffalo G300N V2 CH小测
查看>>
停电遭遇ORA-600
查看>>
ADO.NET与ORM的比较(4):EntityFramework实现CRUD
查看>>
实现Java Web程序的自动登录
查看>>
ASP.NET4.0新特性
查看>>
如何编写更好的SQL查询:终极指南-第三部分
查看>>