分布式配置管理平台VS统一集中配置管理

在大型集群和分布式应用中,配置不宜分散到节点中,应该集中管理,为各种业务平台提供统一的配置管理服务。
随着业务的发展,应用系统中的配置通常会越来越多,常见的一些应用配置大致会有数据源配置,数据源组件配置,业务组件配置等,对于这类配置都会比较稳定且较少变化,通常会放在文件中随应用一起发布。但实际中会有某些配置信息变化有一定频率和规律,并且希望能够做到尽量实时,比如一些营销类,或活动类应用系统,若使用传统的配置文件,加上重新发布应用可能会有些不方便,因此,才有了分布式配置管理平台,旨在能更好地解决这类问题。

统一配置管理 VS 分布式管理配置
(中央管理VS去中心化) 

这个技术话题让我们想起了svn和git之争,统一管理和分布式之间的那些事儿,各有千秋。

 编程开发+脚本 或者借助第三方开源自动化工具 

spring cloud/dubbo/autoconfig  +docker+k8s+ansible/saltstack  目前业界通用的解决方案

Disconf+ZooKeeper+Spring (360和淘宝也开源了自己的统一配置管理)
XXL-CONF
diablo 
ansible-playbook
Fourinone
saltstack
DCMP

 推荐用 分布式管理配置  现在开源社区很多  你可以去搜一哈 
选适合自己的  别人推荐的 不一定是最好的 

 分布式配置管理 - 开源中国社区 https://www.oschina.net/search?scope=all&q=%E5%88%86%E5%B8%83%E5%BC%8F%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86 

分布式配置管理平台的设计与实现


随着业务的发展,应用系统中的配置通常会越来越多,常见的一些应用配置大致会有数据源配置,数据源组件配置,业务组件配置等,对于这类配置都会比较稳定且较少变化,通常会放在文件中随应用一起发布。但实际中会有某些配置信息变化有一定频率和规律,并且希望能够做到尽量实时,比如一些营销类,或活动类应用系统,若使用传统的配置文件,加上重新发布应用可能会有些不方便,因此,才有了分布式配置管理平台,旨在能更好地解决这类问题。本文将介绍相关细节,及一个轻量的开源实现(https://github.com/ihaolin/diablo)

分布式配置平台的一些应用场景

分布式配置,也即配置中心。通常有以下的场景或需求,可以需要考虑使用分布式配置:

  • 对某些配置的更新,不想要重启应用,并且能近似实时生效;

  • 希望将配置进行统一管理,而非放入各应用的配置文件中;

  • 对于某些应用系统,其某些配置变更比较频繁,规律;

  • 通常配置中心也可作为在其他分布式应用中的感知组件,比如典型的Zookeeper;

  • ...。

分布式配置平台需要满足的一些基本特性

对于一个可靠的分布式配置平台,大致应该满足一些基本特性,如:

  • 高可用性:服务器集群应该无单点故障,即只要集群中还有存活的节点,就能提供服务;

  • 容错性:容错性主要针对客户端,应保证即便在配置平台不可用时,也不影响客户端的正常运行;

  • 高性能:对于配置平台,主要操作则是获取配置,不能因为获取配置给应用带来不可接受的损失;

  • 可靠的存储:这包括数据的备份容灾,一致性等,通过数据库和一些运维手段可以解决;

  • 近似实时生效:对于配置的变更,客户端应用能够及时感知;

  • 负载均衡:为了尽量提升服务器集群的性能及稳定性,应尽量保证客户端的请求能尽量均衡负载到各服务器节点;

  • 扩展性:服务器集群应该保证做到无感扩容,以提升集群服务能力;

  • ...。

分布式配置平台Diablo的设计与实现

Diablo架构设计

Diablo的架构设计比较简单轻量,如图:

  • Apps:及各类业务应用实例;

  • Servers:为客户端提供获取配置服务的Server集群;

  • Redis Storage Service:用作数据存储。

Diablo模型设计

  • 应用(App):通常,对于一个用于各外部系统的平台,都可以抽象这些系统为应用,用于标识或是分组。对于应用环境,个人觉得应交由应用去处理,而不是在平台为各应用提供不同环境,比如可以通过应用名称就能区分不同环境。除此外,通常需要让应用提供一些安全方面的配置,如签名Key,加密Key等,可用于在与外部应用交互中作一些安全处理(如签名,加密等);

  • 配置项(Config):对于配置平台,其数据模型比较简单,核心数据就是配置项。配置项除了基本的配置名称和配置值外,通常还需要有用于判定配置项是否变更的字段,可以用MD5值等。

对等的服务器集群

Diablo集群中的Server被视为是对等的,即各节点没有主从(Master/Slave)关系,是逻辑相等的,这样就避免了Master/Slave架构带来的问题,如数据同步延迟,数据丢失等问题。

高性能处理

客户端应用获取配置时,仅会从本地缓存中获取,开发人员在控制台更改配置后,会通知客户端刷新缓冲;

使用Redis作存储

配置平台本身并没有太多复杂的关联关系,因此使用NoSQL也能满足常用的查询。在设计存储Key上,因尽量保证Key的简洁清晰,比如,存储应用记录,可以以apps:1来标识一条记录,而存储结构最好使用hash,而不是使用JSON字符串。在使用Redis时,diablo避免使用了一些特殊函数,如管道,事务等,因为这些函数在一些Redis的高可用解决方案(如Redis Cluster,Redis Proxy等)中通常不支持,这样用户可以自由使用不同的Redis高可用方案。

客户端的实现

  • 重试等待:diablo会通过重试等待等机制保证,在服务端集群不可用时,也不会影响客户端应用的正常运行,而是等待集群恢复;

  • 请求负载:为了使服务器集群的各节点的负载尽量均衡,在客户端进行请求处理前,服务端会为客户端分配一个可用的Server节点,后续的请求将在该节点上,这里使用的负载算法是一致性哈希;

  • 额外参数:通常对于客户端实现,在与服务器交互过程中,除了必要的数据外,还会携带一些额外参数,如客户端版本号(为后期作兼容性处理),语言类型(后端可针对不同语言进行处理)等。

配置更新实时生效

配置更新实时生效是配置平台的核心功能之一,对于获取配置的方式,大致有两种模式:

  • Pull模式:即客户端主动向服务端拉取配置信息,通常是客户端定时轮询拉取。这种方式最简单稳定,但是存在时间间隔问题,间隔太长,配置延迟更新越大,间隔太短对服务器会造成过多的压力;

  • Push模式:即当配置发生变更时,服务端主动推送更新至各个客户端。这种方式能保证配置更新实时生效,但需要在客户端/服务端建立长链接,服务端需要处理各种异常情况和协议规范,保证更新能实时推送成功,实现起来相对麻烦些。

diablo使用了特殊Pull模式,即(长轮询)Long Pulling。当客户端发起Http请求后,服务端接收处理完请求,并不会立即返回客户端,而是等待一定条件发生或超时后才返回客户端,这里的一定条件就是当有配置发生变更时,这样就有效减少了客户端请求,也达到了实时生效的目的。对于Java长连接的实现,主要使用了Servlet规范中的AsyncContext,使得服务端接收到请求后,并不会让Servlet容器立即返回客户端,而是当调用AsyncContext.complete()方法时,才会返回。

客户端语言类型

diablo默认实现了Java语言的客户端,希望以后能支持node,go,python等语言。由于diablo仅通过Http接口提供服务,不同语言只要遵循API接口,即可实现不同的语言版本。若读者有意实现,可参考该客户端规范,下图为客户端与服务端的交互过程:

diablo实践建议

服务器集群部署:对于一个高可用的服务器集群,建议可以nginx等代理服务器作转发,这样在服务器集群发生变化时,客户端应用也不用更新任何配置,如:

应用环境区分:对于需要区分不同环境的应用,可以通过不同的应用名称作区分,如app_dev,app_test。但对于生产环境,都建议与其他环境隔离,独立部署。

总结

以上,则是有关分布式配置管理平台设计与实现的相关细节,也欢迎issue和fork项目diablo。



[案例分析讲解]

   案例一、  为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案。结合.net项目具体情况,实现了配置发布的统一化,对配置进行持久化管理并对外提供restful接口,在此基础上,基于ZooKeeper实现对配置更改的实时推送。系统参考了百度的Disconf,实现和改进了部分功能,是Disconf的.Net精简版,功能有待进一步完善。

这里主要使用到disconf分布式配置管理平台 支持window和linux下面是大家window环境步骤和一些操作总结。

  所需环境:Windows、nginx1.8.1、redis3.0.5、zookeeper3.4.6、mysql5.7 、python2.7.11、Git-2.6.4-64-bit.exe

之前一直采用properties文件管理配置信息,若是集群则每个机器上都要拷贝一份,每次修改也需要依次修改。一直在寻找统一修改,实时生效,方便修改,分环境分系统的配置管理,自己也在整理设计,若找不到合意的就准备自己写一个,可以根据自己需求慢慢改进。通过开源中国微博知道了360的配置管理,看了下没大搞明白,貌似管理不太方便,反正不是我想要的,后来知道了百度的disconf,淘宝也有一个配置管理。我先看了百度的disconf,这就是我想要的,所以没看淘宝那个配置管理。

  首先这是一个开源项目,托管在github上,地址: https://github.com/knightliao/disconf,官方的文档还是很丰富的,地址:https://github.com/knightliao/disconf/wiki 。建议先看官方文档,文档很实用,花不了多少时间,我这里仅就官方没说,但刚接触这个的人常见的部分问题说说自己的解决方案,下面是一张运行效果图。

  

  要看这个项目,需要的知识:java相关技术、前端、git、mysql、tomcat、redis、zookeper、nginx,后面几个简单度一下就能了解个大概。

  1. 安装git客户端、下载代码、导入eclipse、运行redis、zookeper、mysql就不说了。
  2. windows上运行sh脚本小知识。
    一看项目内容就知道,这应该只考虑了Linux环境开发,只提供了sh脚本,而很多人都是windows开发环境。其实安装git客户端后,windows下是可以运行sh脚本的。如下图就是git下的sh软件和运行效果,需要先按官方教程配置环境变量,我换系统了所以没配置,之前配置过。

  3. 能不安装nginx吗?
    这是我刚开始在官方讨论群提的问题,得到的答案是不能,提到了什么动静分离,于是百度了解了下,对nginx在这里扮演的角色有了一个了解,知道他做了什么,才能知道他是否必须。了解了之后,就会知道,这里应该有多中方式实现不安装nginx,我实现了一种如下图所示,其他方式可以百度springMVC关于静态文件的处理方式,第一张截图就是我在eclipse中用tomcat运行的结果。这个能方便开发,正式环境建议还是按官方设计的方式使用,nginx对静态文件的处理要比tomcat快不少。
  4. 看交流群讨论,记录如下几点,可以研究下,看怎么修改能解决问题,然后推送官方,也贡献自己的一份力。
    1) 貌似使用spring4时有问题。
    2) 有人建议添加配置优先级,先读取环境变量,再各种配置文件,都没有时提供默认配置。

  由于官方文档比较详细,这里基本没有提到disconf本身的用途,使用方式。建议到处问人前先仔细看看官方教程。

案例二、

1.系统设计

1.1设计理念

      l  简单易用,用户体验良好

      l  支持配置(KV配置项+配置文件)的分布式化管理

      l  配置发布、更新统一化:用户统一在平台上进行发布、更新配置。

      l  配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。

 

      系统结构图如下: 

 

 

      初始化时,业务流程图如下:

 

      配置更新时,业务流程图如下:

 

1.2.功能介绍

      系统模块架构图如下:

 

1.2.1Client

  配置管理模块:统一管理用户实例中本地配置文件和配置项

  下载模块:restful风格的下载配置文件和配置项

  watch模块:监控远程配置文件和配置项的变化

1.2.2Web

配置管理模块:支持配置模板(配置项或配置文件)的上传、下载、更新

配置存储模块:管理所有配置的存储和读取,根据appName、version、environment来区分项目配置

通知模块:当配置更新后,实时通知使用这些配置的所有实例

权限控制:web用户的权限控制

2.客户端应用

2.1添加clientConfig配置节点

在app.config或者web.config中的configSections节点下添加配置

<section name=”clientConfig” type=”Disconf.Net.Client.ClientConfigSection,Disconf.Net.Client”/>

然后在appSettings同级别的节点上添加clientConfig配置,示例如下

复制代码
<configSections>

 <section name=”clientConfig” type=”Disconf.Net.Client.ClientConfigSection,Disconf.Net.Client”/>

</configSections>

<appSettings file=”appSettings.config”/>

<clientConfig configSource=”clientConfig.config”/>
复制代码

 

2.2clientConfig配置说明

具体示例如下:

复制代码
<clientConfig webApiHost=”http://192.168.1.100:8088/” enableRemote=”true”>

<clientInfo appName=”consoletest” environment=”Dev” version=”1.0.0.0” clientName=”Console_1”/>

<updateStrategy fileIgnores="notdown.txt" itemIgnores="aa,bb,cc " startedSync="true" retryTimes="3" retryIntervalSeconds="10" />

<preservation absolutePath="false" tmpRootDirectory="Tmp\Download\Configs" factRootDirectory="" tmpItemsLocalName="~items.xml" tmpFilesLocalName="~files.txt"/>

</clientConfig>
复制代码

 

 

节点名称

必配

默认值

节点描述

webApiHost

 

Y

 

Rest服务器域名地址

enableRemote

 

N

true

是否启用远程配置,默认true,设为false的话表示不从远程服务器下载配置

clientInfo

appName

Y

 

客户端程序名称,注意大小写要与服务端一致

environment

Y

 

当前客户端程序所处环境,注意大小写要与服务端一致

version

Y

 

当前客户端程序版本,注意大小写要与服务端一致

clientName

N

 

客户端标识,用于服务端查看已更新客户端,如果不设置则默认获取客户端电脑名称

updateStrategy

fileIgnores

N

 

要忽略更新的文件配置,以,分割,注意大小写要与服务端一致

itemIgnores

N

 

要忽略更新的键值对配置,以,分割,注意大小写要与服务端一致

startedSync

N

true

启动时是否同步加载,默认同步

retryTimes

N

3

当获取失败时的重试次数

retryIntervalSeconds

N

10

每次重试时间间隔,单位秒

preservation

absolutePath

N

false

是否绝对路径,默认false。当false时,表示默认以

AppDomain.CurrentDomain.BaseDirectory为比较点,注意:该配置同时适用于TmpRootDirectory、

FactRootDirectory,即要么都只能绝对路径,要么都只能相对路径

tmpRootDirectory

N

Tmp/Download/Configs

下载下来的配置临时保存文件夹根目录

factRootDirectory

N

Configs

配置文件实际所在的根目录

tmpItemsLocalName

N

~items.xml

在临时目录下用于保存所有键值对的文件名,设置为空表示不保存,文件保存在TmpRootDirectory目录下,所以注意不要与

实际配置文件名字冲突

 

tmpFilesLocalName

N

~files.txt

在临时目录下用于保存所有文件配置名的文件名,设置为空表示不保存,文件保存在TmpRootDirectory目录下,所以注意不要与实际配置文件名字冲突

 

2.3Rules

除了配置外,还需要设置更新策略,客户端才能进行配置更新。目前,Rules设置仅支持编码的方式进行,Rule分两种:FileRule,ItemRule,下面分别进行描述:

FileRule:用于设置如何更新文件类型配置,其包含以下方法

方法名

描述

IFileRule MapTo(string refreshSectionName)

注册Rule规则,设置默认的文件配置映射

参数refreshSectionName表示更新回调时,

ConfigurationManager.RefreshSection要刷新的节点名称,默认采用远程配置的configName

IFileRule RefreshIgnores()

不自动调用ConfigurationManager.RefreshSection方法更新配置

IFileRule CallBack(Action action)

当文件下载完成并且替换本地对应文件后回调,注意此处将采用委托链的方式,即多次调用均会被执行

ItemRule:用于设置如何更新键值对类型配置,其包含以下方法

方法名

描述

IItemRule MapTo(string propName)

注册Rule规则,设置默认的属性映射参数

propName表示要赋值的属性名,默认采用远程的configName

IItemRule SetProperty<T>(T entity, string propName = null, Func<string, object> typeConvert = null)

更新指定实体的属性值,按默认方式获取实例属性,注意此处多次调用均会被执行

IItemRule SetProperty(object entity, PropertyInfo prop, Func<string, object> typeConvert = null)

更新指定实体的属性值,注意此处多次调用均会被执行

IItemRule SetStaticProperty<T>(string propName = null, Func<string, object> typeConvert = null)

更新静态属性的值,按默认方式获取静态属性,注意此处多次调用均会被执行

IItemRule SetStaticProperty(PropertyInfo prop, Func<string, object> typeConvert = null)

更新静态属性的值,注意此处多次调用均会被执行

IItemRule CallBack(Action<string> action)

当值发生变更时如何进行回调,注意此处将采用委托链的方式,即多次调用均会被执行

2.4ConfigManager

该类为Client配置入口,通过Singleton提供唯一实例,除了提供Rules的配置入口外,还提供异常通知的事件

要使Disconf.Net.Client工作,必须显示执行指定方法manager.Init(),而在init之前,还需设置Rule和Fault,可以通过ConfigManager.Instance来获取该类的实例对象,然后通过对应的Rule进行相关Rule设定,示例如下:

复制代码
//要更新的文件

ConfigManager.Instance.FileRules.For("appSettings.config").CallBack(() => {

      Console.WriteLine("File changed notice twice");

});


//要更新的键值对

ConfigManager.Instance.ItemRules.For("Dai").MapTo("Person").SetStaticProperty<Program>().CallBack(v =>{

Console.WriteLine("Now item value:{0}", v);

Console.WriteLine("Program.Person is {0} now", Program.Person);

    if (v.Length > 3)

    {

        throw new Exception("Too Long");

    }

});


//忽略更新到本地的键值对

ConfigManager.Instance.ItemRules.For("Peng").CallBack(v =>{

Console.WriteLine("Now item value:{0}", v);

});


//异常处理

ConfigManager.Instance.Faulted+=Manager_Faulted;

//Config初始化,包括ZooKeeper、scan等

ConfigManager.Instance.Init();
复制代码

 

需要特别说明的是:

1、File因为属于下载后覆盖指定位置文件的方式,所以对于Rule可以设置默认规则,如例子中的appSettings.config,其对应的就是config文件中的appSettings部分,此时如果不需要进行CallBack调用,且文件名称(去除后缀)部分与Section一致,那么这部分Rule设置可以忽略,程序会在初始化时自动进行默认设置,而对于Item,因为无法确认更新策略,所以如果不设置Rule,那么就算从服务端获取到了值,该部分也只能被忽略。

2、对于异常部分,程序只是简单的通过Faulted事件来传递异常信息,该事件只有一个Exception类型的参数。

3.web端应用

配置步骤:

1、  创建具体应用(项目)

2、  创建应用的配置模板(1~n个配置,如appSetting.config、redisconfig.config、rabbitMQConfig.config等配置模板)

3、  创建应用的环境(如:开发环境、测试环境、仿真环境等),修改相关的配置

4、  启用对应的配置

5、  至此,client端就可以获取应用环境对应的所有配置

3.1登录

登陆进入配置管理界面

3.2应用

 

 

【新建】:填写应用名称,应用描述保存完成新建,返回可返回应用管理首页。

【初始化ZooKeeper】:第一次启动时Zookeeper初始化。

【编辑】:与新建界面一致,可修改应用名称,应用描述,保存即返回应用管理首页。

【编辑环境】:进入环境环境配置管理首页。

【删除】:删除对应应用记录。

3.3模板

显示所有模板,操作环境配置前,需要先配置模板,根据模板对相应环境的配置进行操作。

 

【新建】:新增模板,填写模板名称、描述、类型、默认值版本号等,如选择文件类型。可上传文件读取文件内容,版本号可以选择已经有的版本号,或者新建版本号。

【编辑】:操作同新建模板,可对模板内容进行修改。

【删除】:点击删除可删除对应模板记录,如该模板在环境中存在配置项,则该模板不允许删除,需删除对应该模板的配置项,才可以删除对应模板。

3.4环境

【新增环境】点击加号可以新增环境,填写环境名称,描述保存即可。

 

【编辑环境】在对应环境上点击鼠标右键即可弹出编辑菜单,点击Edit即可编辑环境,可以修改名称内容等。

 

【配置首页】:配置首页根据版本进行分类,默认显示头部第一个版本,点击其他版本可以进行切换,显示的配置项是模板默认配置项,点击启用即可个性化赋值,针对不同环境进行不同的赋值。编辑可编辑相应配置,禁用等同于删除配置。

 

【启用配置】:名称默认值不能修改,可以点击使用默认值,直接赋值,也可以上传文件使用文件内容,保存即可。

【编辑配置】:操作同启用配置,保存即可修改值。

【禁用配置】:禁用等同于删除配置,删除对应模板配置项,可删除对应模板。

3.5角色

【角色首页】:

Ø  角色首页展示角色列表,角色分为超级管理员和非超级管理员;

Ø  超级管理员角色不展示;

Ø  超级管理员可以看到所有非超级管理员角色,非超级管理员只可以看到当前角色用户创建的角色;

Ø  可以新增角色,也可以对角色进行编辑,只有在创建用户时勾选是否为系统管理员才可以进行角色管理。

【新建角色】:

Ø  新建角色输入角色名称,可以勾选的权限为当前用户所拥有的权限;

Ø  新建的角色作为该用户的下属角色,可分配给当前用户新建的用户;

Ø  父级权限为新建应用所增加的权限,以后每增加一个环境,就相应的增加该应用下的该环境权限,除超级管理员外的角色需对应勾选该权限才能看到该应用或者该权限,保存角色即可。

【编辑角色】:操作同新建角色,可以对该角色进行名称修改,权限修改。

3.6用户

管理用户首页,显示所有用户,可进行新建,编辑用户等操作。

 

【新建用户】:填写姓名,用户名,密码,选择角色(拥有对应角色权限、且可以选择的角色为当前登陆用户新建的角色),选择是否为系统管理员(系统管理员拥有新建用户、新建角色权限),保存即可。

【编辑用户】:操作同新建用户,保存即可修改。


案例三、

《分布式配置管理平台XXL-CONF》

一、简介

1.1 概述

XXL-CONF 是一个分布式配置管理平台,其核心设计目标是“为分布式业务提供统一的配置管理服务”。现已开放源代码,开箱即用。

1.2 特性

  • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
  • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
  • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
  • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
  • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
  • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
  • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
  • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;
  • 9、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

1.3 背景

why not properties

常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置、redis地址配置、活动开关、阈值配置、黑白名单……等等。使用properties维护配置信息将会导致以下几个问题:

  • 1、需要手动修改properties文件;
  • 2、需要重新编译打包;
  • 3、需要重启线上服务器 (项目集群时,更加令人崩溃) ;
  • 4、配置生效不及时: 因为流程复杂, 新的配置生效需要经历比较长的时间才可以生效;
  • 5、不同环境上线包不一致: 例如JDBC连接, 不同环境需要差异化配置;

why XXL-CONF

  • 1、不需要 (手动修改properties文件) : 在配置管理中心提供的Web界面中, 定位到指定配置项, 输入新的配置的值, 点击更新按钮即可;
  • 2、不需要 (重新编译打包) : 配置更新后, 实时推送新配置信息至项目中, 不需要编译打包;
  • 3、不需要 (重启线上服务器) : 配置更新后, 实时推送新配置信息至项目中, 实时生效, 不需要重启线上机器; (在项目集群部署时, 将会节省大量的时间, 避免了集群机器一个一个的重启, 费时费力)
  • 4、配置生效 "非常及时" : 点击更新按钮, 新的配置信息将会即可推送到项目中, 瞬间生效, 非常及时。比如一些开关类型的配置, 配置变更后, 将会立刻推送至项目中并生效, 相对常规配置修改繁琐的流程, 及时性可谓天壤之别;
  • 5、不同环境 "同一个上线包" : 因为差异化的配置托管在配置中心, 因此一个上线包可以复用在生产、测试等各个运行环境, 提供能效;

1.4 下载

源码地址 (将会在两个git仓库同步发布最新代码)
中央仓库地址 (最新Release版本)
<dependency>
  <groupId>com.xuxueli</groupId>
  <artifactId>xxl-conf-core</artifactId>
  <version>1.3.0</version>
</dependency>
博客地址 (将会在两个博客同步更新文档)
技术交流群 (仅作技术交流)
  • 群4:464762661 image
  • 群3:242151780 (群即将满,请加群4)
  • 群2:438249535 (群即将满,请加群4)
  • 群1:367260654 (群即将满,请加群4)

1.5 环境

  • Maven3+
  • Jdk1.7+
  • Tomcat7+
  • Zookeeper3.4+
  • Mysql5.5+

二、快速入门

2.1 初始化“数据库”

请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。脚本位置如下:

xxl-conf/db/xxl-conf.sql

2.2 编译源码

解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下图所示:

输入图片说明

  • xxl-conf-admin:配置管理中心
  • xxl-conf-core:公共依赖
  • xxl-conf-example: 接入XXl-CONF的Demo项目

2.3 “配置管理中心” 项目配置

项目:xxl-conf-admin
作用:管理线上配置信息

配置文件位置:

xxl-conf/xxl-conf-admin/src/main/resources/xxl-config-admin.properties

配置项目说明:

# xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
xxl.conf.zkserver=127.0.0.1:2181

# xxl-conf, jdbc    (配置中心mysql地址)
xxl.conf.jdbc.driverClass=com.mysql.jdbc.Driver
xxl.conf.jdbc.url=jdbc:mysql://localhost:3306/xxl-conf?Unicode=true&amp;characterEncoding=UTF-8
xxl.conf.jdbc.username=root
xxl.conf.jdbc.password=root_pwd

# xxl-conf, admin login (管理中心登录账号密码)
xxl.conf.login.username=admin
xxl.conf.login.password=123456

2.4 “接入XXL-CONF的Demo项目” 项目配置

项目:xxl-conf-example
作用:供用户参考学习如何接入XXL-CONF
A、引入maven依赖
<!-- xxl-conf-client -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-conf-core</artifactId>
    <version>${xxl.conf.version}</version>
</dependency>
B、配置 “XXL-CONF配置解析器”

可参考配置文件:

/xxl-conf/xxl-conf-example/src/main/resources/spring/applicationcontext-xxl-conf.xml

配置项说明

<!-- XXL-CONF配置解析器 -->
<bean id="xxlConfPropertyPlaceholderConfigurer" class="com.xxl.conf.core.spring.XxlConfPropertyPlaceholderConfigurer" />
C、设置 "xxl-conf.properties"

可参考配置文件:

/xxl-conf/xxl-conf-example/src/main/resources/xxl-conf.properties

配置项说明

# xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
xxl.conf.zkserver=127.0.0.1:2181

该配置文件,除了支持配置ZK地址,还可以配置一些本地配置。 XXL-CONF 加载配置时会优先加载 "xxl-conf.properties" 中的配置, 然后才会加载ZK中的配置。可以将一些希望存放本地的配置存放在该文件。

2.5 新增配置分组

输入图片说明

每个配置分组对应一个唯一的GroupName,作为该分组下配置的统一前缀。在“分组管理”栏目可以创建并管理配置分组信息,系统已经提供一个默认分组.

2.6 新增配置信息

登录"配置管理中心"

输入图片说明

进入"配置管理界面",点击"新增配置"按钮

输入图片说明

在弹出界面,填写配置信息

输入图片说明

至此, 一条配置信息已经添加完成.

通过client端,可以实时获取配置信息, 通过本地已经加载过得配置将会接受Zookeeper的更新推送, 如下如日志:

输入图片说明

2.7 项目中使用XXL-CONF

项目: xxl-conf-example:   (可以参考 com.xxl.conf.example.controller.IndexController.index() )
作用: 接入XXl-CONF的Demo项目
  • 方式1: XML文件中的占位符方式

    <bean id="configuration" class="com.xxl.conf.example.core.constant.Configuration">
        <property name="paramByXml" value="${default.key01}" />
    </bean>
    

    特点:

    • 上面配置说明: 在项目启动时, Configuration的paramByXml属性, 会根据配置的占位符${default.key01}, 去XXL-CONF中匹配KEY=key01的配置信息, 赋值给paramByXml;
    • 目前, 该方式配置信息, 只会在项目启动时从XXL-CONF中加载一次, 项目启动后该值不会变更。 例如配置数据连接信息, 如果XXL-CONF平台中连接地址配置改边, 需要重启后才生效;
    • 该方式, 底层本质上是通过 "方式2: API方式" 实现的。
  • 方式2: API方式

    String paramByClient = XxlConfClient.get("default.key02", null);
    

    特点:

    • 上面代码说明: 会获取XXL-CONF平台中KEY=default.key02的配置信息, 如果不存在值使用传递的默认值;
    • 因为Zookeeper会实时推送配置更新到客户端, 因此该方法放回的值可以XXL-CONF平台中的值保持实时一致;
    • XXL-CONF会对Zookeeper推送的配置信息做本地缓存, 该方法查询的是缓存的配置信息, 因此该方法并不会产生性能问题, 使用时不需要考虑性能问题;

三、总体设计

3.1 架构图

输入图片说明

3.2 "配置项" 设计

系统配置信息以K/V的形式存在, "配置项" 属性如下:

  • 分组: "配置项" 的分组, 便于配置分组管理;
  • KEY : "配置项" 的全局唯一标识, 对应一条配置信息;
  • VALUE : "配置项" 中保存的数据信息, 仅仅支持String字符串格式;
  • 描述 : 配置项的描述信息;

每条配置,将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

3.3 "配置中心" 设计

输入图片说明

  • 1、ZK设计: 系统在ZK集群中占用一个根目录 "/xxl-conf", 每新增一条配置项, 将会在该目录下新增一个子节点。结构如下图, 当配置变更时将会触发ZK节点的变更, 将会触发对应类型的ZK广播。
  • 2、数据库备份配置信息: 配置信息在ZK中的新增、变更等操作, 将会同步备份到Mysql中, 进一步保证数据的安全性;
  • 3、配置推送: 配置推送功能在ZK的Watch机制实现。Client在加载一条配置信息时将会Watch该配置对应的ZK节点, 因此, 当对该配置项进行配置更新等操作时, 将会触发ZK的NodeDataChanged广播, Client竟会立刻得到通知并刷新本地缓存中的配置信息;

ZK之watcher普及(来源官方文档,以及网络博客)

1、可以注册watcher的方法:getData、exists、getChildren。
2、可以触发watcher的方法:create、delete、setData。连接断开的情况下触发的watcher会丢失。
3、一个Watcher实例是一个回调函数,被回调一次后就被移除了。如果还需要关注数据的变化,需要再次注册watcher。
4、New ZooKeeper时注册的watcher叫default watcher,它不是一次性的,只对client的连接状态变化作出反应。(推荐ZK初始化时, 主动Watcher如exists)
5、实现永久监听: 由于zookeeper是一次性监听,所以我们必须在wather的process方法里面再设置监听。
6、getChildren("/path")监视/path的子节点,如果(/path)自己删了,也会触发NodeDeleted事件。
《操作--事件》 event For “/path” event For “/path/child”
create(“/path”) EventType.NodeCreated
delete(“/path”) EventType.NodeDeleted
setData(“/path”) EventType.NodeDataChanged
create(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeCreated
delete(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeDeleted
setData(“/path/child”) EventType.NodeDataChanged

《事件--Watch方式》 | Default Watcher | exists(“/path”) | getData(“/path”) | getChildren(“/path”) --- | --- | --- | --- EventType.None | 触发 | 触发 | 触发 | 触发 EventType.NodeCreated | | 触发 | 触发 |
EventType.NodeDeleted | | 触发 | 触发 | EventType.NodeDataChanged | | 触发 | 触发 | EventType.NodeChildrenChanged | | | | 触发

ZooKeeper的一个性能测试

测试数据来自阿里中间件团队

ZK集群情况: 3台ZooKeeper服务器。8核64位jdk1.6;log和snapshot放在不同磁盘;

  • 场景一: pub创建NODE,随后删除

    • 操作: 同一个目录下,先create EPHEMERAL node,再delete;create和delete各计一次更新。没有订阅。一个进程开多个连接,每个连接绑定一个线程,在多个path下做上述操作;不同的连接操作的path不同
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585;
  • 场景二: pub创建NODE, sub订阅并获取数据

    • 操作: 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有3个订阅者连接,一个修改者连接。先全部订阅好。然后每个修改者在自己的每个path下创建一个EPHEMERAL node,不删除;创建前记录时间,订阅者收到event后记录时间(eventStat);重新get到数据后再记录时间(dataStat)。共1000个pub连接,3000个sub连接,20W条数据。收到通知后再去读取数据,五台4核client机器。
    • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-1W+-256ms, 1024-1W+-256, 2048-1W+-270, 4096-8000+-520;
  • 场景三: pub创建NODE,随后设置数据

    • 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有一个修改者连接,没有订阅者。每个修改者在自己的每个path下设置数据。
    • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585 ;

总结: 由于一致性协议带来的额外网络交互,消息开销,以及本地log的IO开销,再加上ZK本身每1000条批量处理1次的优化策略,写入的平均响应时间总会在50-60ms之上。但是整体的TPS还是可观的。单个写入数据的体积越大,响应时间越长,TPS越低,这也是普遍规律了。压测过程中log文件对磁盘的消耗很大。实际运行中应该使用自动脚本定时删除历史log和snapshot文件。

3.4 "配置管理中心" 设计

"配置管理中心" 是 "配置中心" 的上层封装, 提供Web界面供用户对配置信息进行配置查询、配置新增、配置更新和配置删除等操作;

3.5 "客户端" 设计

输入图片说明

API方式加载配置: 客户端主要分为三层:

  • ZK-Client : 第一层为ZK远程客户端的封装, 当业务方项目初始化某一个用到的配置项时, 将会触发ZK-Client对该配置对应节点的Watch, 因此当该节点变动时将会监听到ZK的类似NodeDataChanged的广播, 可以实时获取最新配置信息;
  • Ehcache : 第二层为客户端本地缓存, 可以大大提高系统的并发能力, 当配置初始化或者接受到ZK-Client的配置变更时, 将会把配置信息缓存只Encache中, 业务中针对配置的查询都是读缓存方式实现, 降低对ZK集群的压力;
  • Client-API : 第三层为暴露给业务方使用API, 简单易用, 一行代码获取配置信息, 同时可保证API获取到的配置信息是实时最新的配置信息;

(API方式加载配置, 因为底层做了配置本地缓存, 因此可以放心应用在业务代码中, 不必担心并发压力。完整的支持配置实时推送更新)

输入图片说明

Bean方式加载配置:

系统会在Spring容器中追加一个"PropertyPlaceholderConfigurer"属性解析器, 内部通过自定义的"StringValueResolver"解析器解析配置占位符 "${...}", 匹配到的配置信息将调用"XXL-CFONF"的API客户端加载最新配置信息进行Bean对象的属性赋值,最终完成实例化过程。

(Bean方式加载配置,仅仅在实例化时加载一次; 考虑都实例化后的对象通常为持久化对象, 如数据库连接池对象, 不建议配置的太灵活, 因此Bean类型配置更新需要重启机器)

四、历史版本

4.1 版本1.1.0新特性

  • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
  • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
  • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
  • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
  • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
  • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
  • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
  • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;

4.2 版本1.2.0新特性

  • 1、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

4.3 版本1.3.0新特性

  • 1、支持在线维护配置分组;
  • 2、项目groupId从com.xxl迁移至com.xuxueli,为推送maven中央仓库做准备;
  • 3、v1.3.0版本开始,推送公共依赖至中央仓库;

4.4 版本1.3.1新特性(Coding)

  • zookeeper地址方式从磁盘迁移至项目内;

TODO LIST

  • 1、权限管理:以分组为权限最小单元,只有分组的成员用户才有权限进行对应的配置操作;
  • 2、zookeeper客户端优化, 或将改用zkclient或者curator;
  • 3、local cache 备份到磁盘;zk异常且local properties未配置时,从磁盘上读取配置;

五、其他

5.1 报告问题

XXL-CONF托管在Github上,如有问题可在 ISSUES 上提问,也可以加入上文技术交流群;

5.2 接入登记

更多接入公司,欢迎在github 登记


案例四、

DCMP 详细介绍

DCMP是分布式配置管理平台。提供了一个etcd的管理界面,可通过界面修改配置信息,借助confd可实现配置文件的同步。

安装 && 启动

> go get github.com/silenceper/dcmp
> ./service.sh

界面预览

访问 http://127.0.0.1:8000/


Ø  超级管理员角色不展示;

Ø  超级管理员可以看到所有非超级管理员角色,非超级管理员只可以看到当前角色用户创建的角色;

Ø  可以新增角色,也可以对角色进行编辑,只有在创建用户时勾选是否为系统管理员才可以进行角色管理。

【新建角色】:

Ø  新建角色输入角色名称,可以勾选的权限为当前用户所拥有的权限;

Ø  新建的角色作为该用户的下属角色,可分配给当前用户新建的用户;

Ø  父级权限为新建应用所增加的权限,以后每增加一个环境,就相应的增加该应用下的该环境权限,除超级管理员外的角色需对应勾选该权限才能看到该应用或者该权限,保存角色即可。

【编辑角色】:操作同新建角色,可以对该角色进行名称修改,权限修改。

3.6用户

管理用户首页,显示所有用户,可进行新建,编辑用户等操作。

 

【新建用户】:填写姓名,用户名,密码,选择角色(拥有对应角色权限、且可以选择的角色为当前登陆用户新建的角色),选择是否为系统管理员(系统管理员拥有新建用户、新建角色权限),保存即可。

【编辑用户】:操作同新建用户,保存即可修改。


案例三、

《分布式配置管理平台XXL-CONF》

一、简介

1.1 概述

XXL-CONF 是一个分布式配置管理平台,其核心设计目标是“为分布式业务提供统一的配置管理服务”。现已开放源代码,开箱即用。

1.2 特性

  • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
  • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
  • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
  • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
  • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
  • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
  • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
  • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;
  • 9、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

1.3 背景

why not properties

常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置、redis地址配置、活动开关、阈值配置、黑白名单……等等。使用properties维护配置信息将会导致以下几个问题:

  • 1、需要手动修改properties文件;
  • 2、需要重新编译打包;
  • 3、需要重启线上服务器 (项目集群时,更加令人崩溃) ;
  • 4、配置生效不及时: 因为流程复杂, 新的配置生效需要经历比较长的时间才可以生效;
  • 5、不同环境上线包不一致: 例如JDBC连接, 不同环境需要差异化配置;

why XXL-CONF

  • 1、不需要 (手动修改properties文件) : 在配置管理中心提供的Web界面中, 定位到指定配置项, 输入新的配置的值, 点击更新按钮即可;
  • 2、不需要 (重新编译打包) : 配置更新后, 实时推送新配置信息至项目中, 不需要编译打包;
  • 3、不需要 (重启线上服务器) : 配置更新后, 实时推送新配置信息至项目中, 实时生效, 不需要重启线上机器; (在项目集群部署时, 将会节省大量的时间, 避免了集群机器一个一个的重启, 费时费力)
  • 4、配置生效 "非常及时" : 点击更新按钮, 新的配置信息将会即可推送到项目中, 瞬间生效, 非常及时。比如一些开关类型的配置, 配置变更后, 将会立刻推送至项目中并生效, 相对常规配置修改繁琐的流程, 及时性可谓天壤之别;
  • 5、不同环境 "同一个上线包" : 因为差异化的配置托管在配置中心, 因此一个上线包可以复用在生产、测试等各个运行环境, 提供能效;

1.4 下载

源码地址 (将会在两个git仓库同步发布最新代码)
中央仓库地址 (最新Release版本)
<dependency>
  <groupId>com.xuxueli</groupId>
  <artifactId>xxl-conf-core</artifactId>
  <version>1.3.0</version>
</dependency>
博客地址 (将会在两个博客同步更新文档)
技术交流群 (仅作技术交流)
  • 群4:464762661 image
  • 群3:242151780 (群即将满,请加群4)
  • 群2:438249535 (群即将满,请加群4)
  • 群1:367260654 (群即将满,请加群4)

1.5 环境

  • Maven3+
  • Jdk1.7+
  • Tomcat7+
  • Zookeeper3.4+
  • Mysql5.5+

二、快速入门

2.1 初始化“数据库”

请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。脚本位置如下:

xxl-conf/db/xxl-conf.sql

2.2 编译源码

解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下图所示:

输入图片说明

  • xxl-conf-admin:配置管理中心
  • xxl-conf-core:公共依赖
  • xxl-conf-example: 接入XXl-CONF的Demo项目

2.3 “配置管理中心” 项目配置

项目:xxl-conf-admin
作用:管理线上配置信息

配置文件位置:

xxl-conf/xxl-conf-admin/src/main/resources/xxl-config-admin.properties

配置项目说明:

# xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
xxl.conf.zkserver=127.0.0.1:2181

# xxl-conf, jdbc    (配置中心mysql地址)
xxl.conf.jdbc.driverClass=com.mysql.jdbc.Driver
xxl.conf.jdbc.url=jdbc:mysql://localhost:3306/xxl-conf?Unicode=true&amp;characterEncoding=UTF-8
xxl.conf.jdbc.username=root
xxl.conf.jdbc.password=root_pwd

# xxl-conf, admin login (管理中心登录账号密码)
xxl.conf.login.username=admin
xxl.conf.login.password=123456

2.4 “接入XXL-CONF的Demo项目” 项目配置

项目:xxl-conf-example
作用:供用户参考学习如何接入XXL-CONF
A、引入maven依赖
<!-- xxl-conf-client -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-conf-core</artifactId>
    <version>${xxl.conf.version}</version>
</dependency>
B、配置 “XXL-CONF配置解析器”

可参考配置文件:

/xxl-conf/xxl-conf-example/src/main/resources/spring/applicationcontext-xxl-conf.xml

配置项说明

<!-- XXL-CONF配置解析器 -->
<bean id="xxlConfPropertyPlaceholderConfigurer" class="com.xxl.conf.core.spring.XxlConfPropertyPlaceholderConfigurer" />
C、设置 "xxl-conf.properties"

可参考配置文件:

/xxl-conf/xxl-conf-example/src/main/resources/xxl-conf.properties

配置项说明

# xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
xxl.conf.zkserver=127.0.0.1:2181

该配置文件,除了支持配置ZK地址,还可以配置一些本地配置。 XXL-CONF 加载配置时会优先加载 "xxl-conf.properties" 中的配置, 然后才会加载ZK中的配置。可以将一些希望存放本地的配置存放在该文件。

2.5 新增配置分组

输入图片说明

每个配置分组对应一个唯一的GroupName,作为该分组下配置的统一前缀。在“分组管理”栏目可以创建并管理配置分组信息,系统已经提供一个默认分组.

2.6 新增配置信息

登录"配置管理中心"

输入图片说明

进入"配置管理界面",点击"新增配置"按钮

输入图片说明

在弹出界面,填写配置信息

输入图片说明

至此, 一条配置信息已经添加完成.

通过client端,可以实时获取配置信息, 通过本地已经加载过得配置将会接受Zookeeper的更新推送, 如下如日志:

输入图片说明

2.7 项目中使用XXL-CONF

项目: xxl-conf-example:   (可以参考 com.xxl.conf.example.controller.IndexController.index() )
作用: 接入XXl-CONF的Demo项目
  • 方式1: XML文件中的占位符方式

    <bean id="configuration" class="com.xxl.conf.example.core.constant.Configuration">
        <property name="paramByXml" value="${default.key01}" />
    </bean>
    

    特点:

    • 上面配置说明: 在项目启动时, Configuration的paramByXml属性, 会根据配置的占位符${default.key01}, 去XXL-CONF中匹配KEY=key01的配置信息, 赋值给paramByXml;
    • 目前, 该方式配置信息, 只会在项目启动时从XXL-CONF中加载一次, 项目启动后该值不会变更。 例如配置数据连接信息, 如果XXL-CONF平台中连接地址配置改边, 需要重启后才生效;
    • 该方式, 底层本质上是通过 "方式2: API方式" 实现的。
  • 方式2: API方式

    String paramByClient = XxlConfClient.get("default.key02", null);
    

    特点:

    • 上面代码说明: 会获取XXL-CONF平台中KEY=default.key02的配置信息, 如果不存在值使用传递的默认值;
    • 因为Zookeeper会实时推送配置更新到客户端, 因此该方法放回的值可以XXL-CONF平台中的值保持实时一致;
    • XXL-CONF会对Zookeeper推送的配置信息做本地缓存, 该方法查询的是缓存的配置信息, 因此该方法并不会产生性能问题, 使用时不需要考虑性能问题;

三、总体设计

3.1 架构图

输入图片说明

3.2 "配置项" 设计

系统配置信息以K/V的形式存在, "配置项" 属性如下:

  • 分组: "配置项" 的分组, 便于配置分组管理;
  • KEY : "配置项" 的全局唯一标识, 对应一条配置信息;
  • VALUE : "配置项" 中保存的数据信息, 仅仅支持String字符串格式;
  • 描述 : 配置项的描述信息;

每条配置,将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

3.3 "配置中心" 设计

输入图片说明

  • 1、ZK设计: 系统在ZK集群中占用一个根目录 "/xxl-conf", 每新增一条配置项, 将会在该目录下新增一个子节点。结构如下图, 当配置变更时将会触发ZK节点的变更, 将会触发对应类型的ZK广播。
  • 2、数据库备份配置信息: 配置信息在ZK中的新增、变更等操作, 将会同步备份到Mysql中, 进一步保证数据的安全性;
  • 3、配置推送: 配置推送功能在ZK的Watch机制实现。Client在加载一条配置信息时将会Watch该配置对应的ZK节点, 因此, 当对该配置项进行配置更新等操作时, 将会触发ZK的NodeDataChanged广播, Client竟会立刻得到通知并刷新本地缓存中的配置信息;

ZK之watcher普及(来源官方文档,以及网络博客)

1、可以注册watcher的方法:getData、exists、getChildren。
2、可以触发watcher的方法:create、delete、setData。连接断开的情况下触发的watcher会丢失。
3、一个Watcher实例是一个回调函数,被回调一次后就被移除了。如果还需要关注数据的变化,需要再次注册watcher。
4、New ZooKeeper时注册的watcher叫default watcher,它不是一次性的,只对client的连接状态变化作出反应。(推荐ZK初始化时, 主动Watcher如exists)
5、实现永久监听: 由于zookeeper是一次性监听,所以我们必须在wather的process方法里面再设置监听。
6、getChildren("/path")监视/path的子节点,如果(/path)自己删了,也会触发NodeDeleted事件。
《操作--事件》 event For “/path” event For “/path/child”
create(“/path”) EventType.NodeCreated
delete(“/path”) EventType.NodeDeleted
setData(“/path”) EventType.NodeDataChanged
create(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeCreated
delete(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeDeleted
setData(“/path/child”) EventType.NodeDataChanged

《事件--Watch方式》 | Default Watcher | exists(“/path”) | getData(“/path”) | getChildren(“/path”) --- | --- | --- | --- EventType.None | 触发 | 触发 | 触发 | 触发 EventType.NodeCreated | | 触发 | 触发 |
EventType.NodeDeleted | | 触发 | 触发 | EventType.NodeDataChanged | | 触发 | 触发 | EventType.NodeChildrenChanged | | | | 触发

ZooKeeper的一个性能测试

测试数据来自阿里中间件团队

ZK集群情况: 3台ZooKeeper服务器。8核64位jdk1.6;log和snapshot放在不同磁盘;

  • 场景一: pub创建NODE,随后删除

    • 操作: 同一个目录下,先create EPHEMERAL node,再delete;create和delete各计一次更新。没有订阅。一个进程开多个连接,每个连接绑定一个线程,在多个path下做上述操作;不同的连接操作的path不同
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585;
  • 场景二: pub创建NODE, sub订阅并获取数据

    • 操作: 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有3个订阅者连接,一个修改者连接。先全部订阅好。然后每个修改者在自己的每个path下创建一个EPHEMERAL node,不删除;创建前记录时间,订阅者收到event后记录时间(eventStat);重新get到数据后再记录时间(dataStat)。共1000个pub连接,3000个sub连接,20W条数据。收到通知后再去读取数据,五台4核client机器。
    • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-1W+-256ms, 1024-1W+-256, 2048-1W+-270, 4096-8000+-520;
  • 场景三: pub创建NODE,随后设置数据

    • 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有一个修改者连接,没有订阅者。每个修改者在自己的每个path下设置数据。
    • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585 ;

总结: 由于一致性协议带来的额外网络交互,消息开销,以及本地log的IO开销,再加上ZK本身每1000条批量处理1次的优化策略,写入的平均响应时间总会在50-60ms之上。但是整体的TPS还是可观的。单个写入数据的体积越大,响应时间越长,TPS越低,这也是普遍规律了。压测过程中log文件对磁盘的消耗很大。实际运行中应该使用自动脚本定时删除历史log和snapshot文件。

3.4 "配置管理中心" 设计

"配置管理中心" 是 "配置中心" 的上层封装, 提供Web界面供用户对配置信息进行配置查询、配置新增、配置更新和配置删除等操作;

3.5 "客户端" 设计

输入图片说明

API方式加载配置: 客户端主要分为三层:

  • ZK-Client : 第一层为ZK远程客户端的封装, 当业务方项目初始化某一个用到的配置项时, 将会触发ZK-Client对该配置对应节点的Watch, 因此当该节点变动时将会监听到ZK的类似NodeDataChanged的广播, 可以实时获取最新配置信息;
  • Ehcache : 第二层为客户端本地缓存, 可以大大提高系统的并发能力, 当配置初始化或者接受到ZK-Client的配置变更时, 将会把配置信息缓存只Encache中, 业务中针对配置的查询都是读缓存方式实现, 降低对ZK集群的压力;
  • Client-API : 第三层为暴露给业务方使用API, 简单易用, 一行代码获取配置信息, 同时可保证API获取到的配置信息是实时最新的配置信息;

(API方式加载配置, 因为底层做了配置本地缓存, 因此可以放心应用在业务代码中, 不必担心并发压力。完整的支持配置实时推送更新)

输入图片说明

Bean方式加载配置:

系统会在Spring容器中追加一个"PropertyPlaceholderConfigurer"属性解析器, 内部通过自定义的"StringValueResolver"解析器解析配置占位符 "${...}", 匹配到的配置信息将调用"XXL-CFONF"的API客户端加载最新配置信息进行Bean对象的属性赋值,最终完成实例化过程。

(Bean方式加载配置,仅仅在实例化时加载一次; 考虑都实例化后的对象通常为持久化对象, 如数据库连接池对象, 不建议配置的太灵活, 因此Bean类型配置更新需要重启机器)

四、历史版本

4.1 版本1.1.0新特性

  • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
  • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
  • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
  • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
  • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
  • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
  • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
  • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;

4.2 版本1.2.0新特性

  • 1、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

4.3 版本1.3.0新特性

  • 1、支持在线维护配置分组;
  • 2、项目groupId从com.xxl迁移至com.xuxueli,为推送maven中央仓库做准备;
  • 3、v1.3.0版本开始,推送公共依赖至中央仓库;

4.4 版本1.3.1新特性(Coding)

  • zookeeper地址方式从磁盘迁移至项目内;

TODO LIST

  • 1、权限管理:以分组为权限最小单元,只有分组的成员用户才有权限进行对应的配置操作;
  • 2、zookeeper客户端优化, 或将改用zkclient或者curator;
  • 3、local cache 备份到磁盘;zk异常且local properties未配置时,从磁盘上读取配置;

五、其他

5.1 报告问题

XXL-CONF托管在Github上,如有问题可在 ISSUES 上提问,也可以加入上文技术交流群;

5.2 接入登记

更多接入公司,欢迎在github 登记


案例四、

Ø  超级管理员角色不展示;

Ø  超级管理员可以看到所有非超级管理员角色,非超级管理员只可以看到当前角色用户创建的角色;

Ø  可以新增角色,也可以对角色进行编辑,只有在创建用户时勾选是否为系统管理员才可以进行角色管理。

【新建角色】:

Ø  新建角色输入角色名称,可以勾选的权限为当前用户所拥有的权限;

Ø  新建的角色作为该用户的下属角色,可分配给当前用户新建的用户;

Ø  父级权限为新建应用所增加的权限,以后每增加一个环境,就相应的增加该应用下的该环境权限,除超级管理员外的角色需对应勾选该权限才能看到该应用或者该权限,保存角色即可。

【编辑角色】:操作同新建角色,可以对该角色进行名称修改,权限修改。

3.6用户

管理用户首页,显示所有用户,可进行新建,编辑用户等操作。

 

【新建用户】:填写姓名,用户名,密码,选择角色(拥有对应角色权限、且可以选择的角色为当前登陆用户新建的角色),选择是否为系统管理员(系统管理员拥有新建用户、新建角色权限),保存即可。

【编辑用户】:操作同新建用户,保存即可修改。


案例三、

《分布式配置管理平台XXL-CONF》

一、简介

1.1 概述

XXL-CONF 是一个分布式配置管理平台,其核心设计目标是“为分布式业务提供统一的配置管理服务”。现已开放源代码,开箱即用。

1.2 特性

  • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
  • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
  • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
  • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
  • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
  • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
  • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
  • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;
  • 9、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

1.3 背景

why not properties

常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置、redis地址配置、活动开关、阈值配置、黑白名单……等等。使用properties维护配置信息将会导致以下几个问题:

  • 1、需要手动修改properties文件;
  • 2、需要重新编译打包;
  • 3、需要重启线上服务器 (项目集群时,更加令人崩溃) ;
  • 4、配置生效不及时: 因为流程复杂, 新的配置生效需要经历比较长的时间才可以生效;
  • 5、不同环境上线包不一致: 例如JDBC连接, 不同环境需要差异化配置;

why XXL-CONF

  • 1、不需要 (手动修改properties文件) : 在配置管理中心提供的Web界面中, 定位到指定配置项, 输入新的配置的值, 点击更新按钮即可;
  • 2、不需要 (重新编译打包) : 配置更新后, 实时推送新配置信息至项目中, 不需要编译打包;
  • 3、不需要 (重启线上服务器) : 配置更新后, 实时推送新配置信息至项目中, 实时生效, 不需要重启线上机器; (在项目集群部署时, 将会节省大量的时间, 避免了集群机器一个一个的重启, 费时费力)
  • 4、配置生效 "非常及时" : 点击更新按钮, 新的配置信息将会即可推送到项目中, 瞬间生效, 非常及时。比如一些开关类型的配置, 配置变更后, 将会立刻推送至项目中并生效, 相对常规配置修改繁琐的流程, 及时性可谓天壤之别;
  • 5、不同环境 "同一个上线包" : 因为差异化的配置托管在配置中心, 因此一个上线包可以复用在生产、测试等各个运行环境, 提供能效;

1.4 下载

源码地址 (将会在两个git仓库同步发布最新代码)
中央仓库地址 (最新Release版本)
<dependency>
  <groupId>com.xuxueli</groupId>
  <artifactId>xxl-conf-core</artifactId>
  <version>1.3.0</version>
</dependency>
博客地址 (将会在两个博客同步更新文档)
技术交流群 (仅作技术交流)
  • 群4:464762661 image
  • 群3:242151780 (群即将满,请加群4)
  • 群2:438249535 (群即将满,请加群4)
  • 群1:367260654 (群即将满,请加群4)

1.5 环境

  • Maven3+
  • Jdk1.7+
  • Tomcat7+
  • Zookeeper3.4+
  • Mysql5.5+

二、快速入门

2.1 初始化“数据库”

请下载项目源码并解压,获取 "调度数据库初始化SQL脚本" 并执行即可。脚本位置如下:

xxl-conf/db/xxl-conf.sql

2.2 编译源码

解压源码,按照maven格式将源码导入IDE, 使用maven进行编译即可,源码结构如下图所示:

输入图片说明

  • xxl-conf-admin:配置管理中心
  • xxl-conf-core:公共依赖
  • xxl-conf-example: 接入XXl-CONF的Demo项目

2.3 “配置管理中心” 项目配置

项目:xxl-conf-admin
作用:管理线上配置信息

配置文件位置:

xxl-conf/xxl-conf-admin/src/main/resources/xxl-config-admin.properties

配置项目说明:

# xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
xxl.conf.zkserver=127.0.0.1:2181

# xxl-conf, jdbc    (配置中心mysql地址)
xxl.conf.jdbc.driverClass=com.mysql.jdbc.Driver
xxl.conf.jdbc.url=jdbc:mysql://localhost:3306/xxl-conf?Unicode=true&amp;characterEncoding=UTF-8
xxl.conf.jdbc.username=root
xxl.conf.jdbc.password=root_pwd

# xxl-conf, admin login (管理中心登录账号密码)
xxl.conf.login.username=admin
xxl.conf.login.password=123456

2.4 “接入XXL-CONF的Demo项目” 项目配置

项目:xxl-conf-example
作用:供用户参考学习如何接入XXL-CONF
A、引入maven依赖
<!-- xxl-conf-client -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-conf-core</artifactId>
    <version>${xxl.conf.version}</version>
</dependency>
B、配置 “XXL-CONF配置解析器”

可参考配置文件:

/xxl-conf/xxl-conf-example/src/main/resources/spring/applicationcontext-xxl-conf.xml

配置项说明

<!-- XXL-CONF配置解析器 -->
<bean id="xxlConfPropertyPlaceholderConfigurer" class="com.xxl.conf.core.spring.XxlConfPropertyPlaceholderConfigurer" />
C、设置 "xxl-conf.properties"

可参考配置文件:

/xxl-conf/xxl-conf-example/src/main/resources/xxl-conf.properties

配置项说明

# xxl-conf, zk address  (配置中心zookeeper集群地址,如有多个地址用逗号分隔)
xxl.conf.zkserver=127.0.0.1:2181

该配置文件,除了支持配置ZK地址,还可以配置一些本地配置。 XXL-CONF 加载配置时会优先加载 "xxl-conf.properties" 中的配置, 然后才会加载ZK中的配置。可以将一些希望存放本地的配置存放在该文件。

2.5 新增配置分组

输入图片说明

每个配置分组对应一个唯一的GroupName,作为该分组下配置的统一前缀。在“分组管理”栏目可以创建并管理配置分组信息,系统已经提供一个默认分组.

2.6 新增配置信息

登录"配置管理中心"

输入图片说明

进入"配置管理界面",点击"新增配置"按钮

输入图片说明

在弹出界面,填写配置信息

输入图片说明

至此, 一条配置信息已经添加完成.

通过client端,可以实时获取配置信息, 通过本地已经加载过得配置将会接受Zookeeper的更新推送, 如下如日志:

输入图片说明

2.7 项目中使用XXL-CONF

项目: xxl-conf-example:   (可以参考 com.xxl.conf.example.controller.IndexController.index() )
作用: 接入XXl-CONF的Demo项目
  • 方式1: XML文件中的占位符方式

    <bean id="configuration" class="com.xxl.conf.example.core.constant.Configuration">
        <property name="paramByXml" value="${default.key01}" />
    </bean>
    

    特点:

    • 上面配置说明: 在项目启动时, Configuration的paramByXml属性, 会根据配置的占位符${default.key01}, 去XXL-CONF中匹配KEY=key01的配置信息, 赋值给paramByXml;
    • 目前, 该方式配置信息, 只会在项目启动时从XXL-CONF中加载一次, 项目启动后该值不会变更。 例如配置数据连接信息, 如果XXL-CONF平台中连接地址配置改边, 需要重启后才生效;
    • 该方式, 底层本质上是通过 "方式2: API方式" 实现的。
  • 方式2: API方式

    String paramByClient = XxlConfClient.get("default.key02", null);
    

    特点:

    • 上面代码说明: 会获取XXL-CONF平台中KEY=default.key02的配置信息, 如果不存在值使用传递的默认值;
    • 因为Zookeeper会实时推送配置更新到客户端, 因此该方法放回的值可以XXL-CONF平台中的值保持实时一致;
    • XXL-CONF会对Zookeeper推送的配置信息做本地缓存, 该方法查询的是缓存的配置信息, 因此该方法并不会产生性能问题, 使用时不需要考虑性能问题;

三、总体设计

3.1 架构图

输入图片说明

3.2 "配置项" 设计

系统配置信息以K/V的形式存在, "配置项" 属性如下:

  • 分组: "配置项" 的分组, 便于配置分组管理;
  • KEY : "配置项" 的全局唯一标识, 对应一条配置信息;
  • VALUE : "配置项" 中保存的数据信息, 仅仅支持String字符串格式;
  • 描述 : 配置项的描述信息;

每条配置,将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

3.3 "配置中心" 设计

输入图片说明

  • 1、ZK设计: 系统在ZK集群中占用一个根目录 "/xxl-conf", 每新增一条配置项, 将会在该目录下新增一个子节点。结构如下图, 当配置变更时将会触发ZK节点的变更, 将会触发对应类型的ZK广播。
  • 2、数据库备份配置信息: 配置信息在ZK中的新增、变更等操作, 将会同步备份到Mysql中, 进一步保证数据的安全性;
  • 3、配置推送: 配置推送功能在ZK的Watch机制实现。Client在加载一条配置信息时将会Watch该配置对应的ZK节点, 因此, 当对该配置项进行配置更新等操作时, 将会触发ZK的NodeDataChanged广播, Client竟会立刻得到通知并刷新本地缓存中的配置信息;

ZK之watcher普及(来源官方文档,以及网络博客)

1、可以注册watcher的方法:getData、exists、getChildren。
2、可以触发watcher的方法:create、delete、setData。连接断开的情况下触发的watcher会丢失。
3、一个Watcher实例是一个回调函数,被回调一次后就被移除了。如果还需要关注数据的变化,需要再次注册watcher。
4、New ZooKeeper时注册的watcher叫default watcher,它不是一次性的,只对client的连接状态变化作出反应。(推荐ZK初始化时, 主动Watcher如exists)
5、实现永久监听: 由于zookeeper是一次性监听,所以我们必须在wather的process方法里面再设置监听。
6、getChildren("/path")监视/path的子节点,如果(/path)自己删了,也会触发NodeDeleted事件。
《操作--事件》 event For “/path” event For “/path/child”
create(“/path”) EventType.NodeCreated
delete(“/path”) EventType.NodeDeleted
setData(“/path”) EventType.NodeDataChanged
create(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeCreated
delete(“/path/child”) EventType.NodeChildrenChanged(getChild) EventType.NodeDeleted
setData(“/path/child”) EventType.NodeDataChanged

《事件--Watch方式》 | Default Watcher | exists(“/path”) | getData(“/path”) | getChildren(“/path”) --- | --- | --- | --- EventType.None | 触发 | 触发 | 触发 | 触发 EventType.NodeCreated | | 触发 | 触发 |
EventType.NodeDeleted | | 触发 | 触发 | EventType.NodeDataChanged | | 触发 | 触发 | EventType.NodeChildrenChanged | | | | 触发

ZooKeeper的一个性能测试

测试数据来自阿里中间件团队

ZK集群情况: 3台ZooKeeper服务器。8核64位jdk1.6;log和snapshot放在不同磁盘;

  • 场景一: pub创建NODE,随后删除

    • 操作: 同一个目录下,先create EPHEMERAL node,再delete;create和delete各计一次更新。没有订阅。一个进程开多个连接,每个连接绑定一个线程,在多个path下做上述操作;不同的连接操作的path不同
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585;
  • 场景二: pub创建NODE, sub订阅并获取数据

    • 操作: 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有3个订阅者连接,一个修改者连接。先全部订阅好。然后每个修改者在自己的每个path下创建一个EPHEMERAL node,不删除;创建前记录时间,订阅者收到event后记录时间(eventStat);重新get到数据后再记录时间(dataStat)。共1000个pub连接,3000个sub连接,20W条数据。收到通知后再去读取数据,五台4核client机器。
    • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-1W+-256ms, 1024-1W+-256, 2048-1W+-270, 4096-8000+-520;
  • 场景三: pub创建NODE,随后设置数据

    • 一个进程开多个连接,每连接一个线程,每个连接在多个path下做下述操作;不同的连接操作的path不同。每个path有一个修改者连接,没有订阅者。每个修改者在自己的每个path下设置数据。
    • 结果汇总: getAfterNotify=false(只收事件,受到通知后不去读取数据);五台4核client机器
    • 结果数据: "dataSize(字节)-TPS-响应时间(ms)" 统计结果为: 255-14723-82, 1024-7677-280, 4096-2037-1585 ;

总结: 由于一致性协议带来的额外网络交互,消息开销,以及本地log的IO开销,再加上ZK本身每1000条批量处理1次的优化策略,写入的平均响应时间总会在50-60ms之上。但是整体的TPS还是可观的。单个写入数据的体积越大,响应时间越长,TPS越低,这也是普遍规律了。压测过程中log文件对磁盘的消耗很大。实际运行中应该使用自动脚本定时删除历史log和snapshot文件。

3.4 "配置管理中心" 设计

"配置管理中心" 是 "配置中心" 的上层封装, 提供Web界面供用户对配置信息进行配置查询、配置新增、配置更新和配置删除等操作;

3.5 "客户端" 设计

输入图片说明

API方式加载配置: 客户端主要分为三层:

  • ZK-Client : 第一层为ZK远程客户端的封装, 当业务方项目初始化某一个用到的配置项时, 将会触发ZK-Client对该配置对应节点的Watch, 因此当该节点变动时将会监听到ZK的类似NodeDataChanged的广播, 可以实时获取最新配置信息;
  • Ehcache : 第二层为客户端本地缓存, 可以大大提高系统的并发能力, 当配置初始化或者接受到ZK-Client的配置变更时, 将会把配置信息缓存只Encache中, 业务中针对配置的查询都是读缓存方式实现, 降低对ZK集群的压力;
  • Client-API : 第三层为暴露给业务方使用API, 简单易用, 一行代码获取配置信息, 同时可保证API获取到的配置信息是实时最新的配置信息;

(API方式加载配置, 因为底层做了配置本地缓存, 因此可以放心应用在业务代码中, 不必担心并发压力。完整的支持配置实时推送更新)

输入图片说明

Bean方式加载配置:

系统会在Spring容器中追加一个"PropertyPlaceholderConfigurer"属性解析器, 内部通过自定义的"StringValueResolver"解析器解析配置占位符 "${...}", 匹配到的配置信息将调用"XXL-CFONF"的API客户端加载最新配置信息进行Bean对象的属性赋值,最终完成实例化过程。

(Bean方式加载配置,仅仅在实例化时加载一次; 考虑都实例化后的对象通常为持久化对象, 如数据库连接池对象, 不建议配置的太灵活, 因此Bean类型配置更新需要重启机器)

四、历史版本

4.1 版本1.1.0新特性

  • 1、简单易用: 上手非常简单, 只需要引入maven依赖和一行配置即可;
  • 2、在线管理: 提供配置管理中心, 支持在线管理配置信息;
  • 3、实时推送: 配置信息更新后, Zookeeper实时推送配置信息, 项目中配置数据会实时更新并生效, 不需要重启线上机器;
  • 4、高性能: 系统会对Zookeeper推送的配置信息, 在Encache中做本地缓存, 在接受推送更新或者缓存失效时会及时更新缓存数据, 因此业务中对配置数据的查询并不存在性能问题;
  • 5、配置备份: 配置数据首先会保存在Zookeeper中, 同时, 在MySQL中会对配置信息做备份, 保证配置数据的安全性;
  • 6、HA: 配置中心基于Zookeeper集群, 只要集群节点保证存活数量大于N/2+1, 就可保证服务稳定, 避免单点风险;
  • 7、分布式: 可方便的接入线上分布式部署的各个业务线, 统一管理配置信息;
  • 8、配置共享: 平台中的配置信息针对各个业务线是平等的, 各个业务线可以共享配置中心的配置信息, 当然也可以配置业务内专属配置信息;

4.2 版本1.2.0新特性

  • 1、配置分组: 支持对配置进行分组管理, 每条配置将会生成全局唯一标示GroupKey,在client端使用时,需要通过该值匹配对应的配置信息;

4.3 版本1.3.0新特性

  • 1、支持在线维护配置分组;
  • 2、项目groupId从com.xxl迁移至com.xuxueli,为推送maven中央仓库做准备;
  • 3、v1.3.0版本开始,推送公共依赖至中央仓库;

4.4 版本1.3.1新特性(Coding)

  • zookeeper地址方式从磁盘迁移至项目内;

TODO LIST

  • 1、权限管理:以分组为权限最小单元,只有分组的成员用户才有权限进行对应的配置操作;
  • 2、zookeeper客户端优化, 或将改用zkclient或者curator;
  • 3、local cache 备份到磁盘;zk异常且local properties未配置时,从磁盘上读取配置;

五、其他

5.1 报告问题

XXL-CONF托管在Github上,如有问题可在 ISSUES 上提问,也可以加入上文技术交流群;

5.2 接入登记

更多接入公司,欢迎在github 登记


案例五、

分布式配置管理平台Disconf

摘要

为了更好的解决分布式环境下多台服务实例的配置统一管理问题,本文提出了一套完整的分布式配置管理解决方案(简称为disconf[4],下同)。首先,实现了同构系统的配置发布统一化,提供了配置服务server,该服务可以对配置进行持久化管理并对外提供restful接口,在此基础上,基于zookeeper实现对配置更改的实时推送,并且,提供了稳定有效的容灾方案,以及用户体验良好的编程模型和WEB用户管理界面。其次,实现了异构系统的配置包管理,提出基于zookeeper的全局分布式一致性锁来实现主备统一部署、系统异常时的主备自主切换。通过在百度内部以及外部等多个产品线的实践结果表明,本解决方案是有效且稳定的。

技术背景

在一个分布式环境中,同类型的服务往往会部署很多实例。这些实例使用了一些配置,为了更好地维护这些配置就产生了配置管理服务。通过这个服务可以轻松地管理成千上百个服务实例的配置问题。

王阿晶提出了基于zooKeeper的配置信息存储方案的设计与实现[1], 它将所有配置存储在zookeeper上,这会导致配置的管理不那么方便,而且他们没有相关的源码实现。淘宝的diamond[2]是淘宝内部使用的一个管理持久配置的系统,它具有完整的开源源码实现,它的特点是简单、可靠、易用,淘宝内部绝大多数系统的配置都采用diamond来进行统一管理。他将所有配置文件里的配置打散化进行存储,只支持KV结构,并且配置更新的推送是非实时的。百度内部的BJF配置中心服务[3]采用了类似淘宝diamond的实现,也是配置打散化、只支持KV和非实时推送。

同构系统是市场的主流,特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多。但是,异构系统也有一定的存在意义,譬如,对于“拉模式”的多个下游实例,同一时间点只能只有一个下游实例在运行。在这种情景下,就存在多台实例机器有“主备机”模式的问题。目前国内并没有很明显的解决方案来统一解决此问题。

功能特点与设计理念

disconf是一套完整的基于zookeeper的分布式配置统一解决方案。

它的功能特点是

  • 支持配置(配置项+配置文件)的分布式化管理
    • 配置发布统一化
    • 配置发布、更新统一化(云端存储、发布):配置存储在云端系统,用户统一在平台上进行发布、更新配置。
    • 配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。特殊地,如果用户为此配置定义了回调函数类,则此函数类会被自动调用。
  • 配置异构系统管理
    • 异构包部署统一化:这里的异构系统是指一个系统部署多个实例时,由于配置不同,从而需要多个部署包(jar或war)的情况(下同)。使用Disconf后,异构系统的部署只需要一个部署包,不同实例的配置会自动分配。特别地,在业界大量使用部署虚拟化(如JPAAS系统,SAE,BAE)的情况下,同一个系统使用同一个部署包的情景会越来越多,Disconf可以很自然地与他天然契合。 异构主备自动切换:如果一个异构系统存在主备机,主机发生挂机时,备机可以自动获取主机配置从而变成主机。
    • 异构主备机Context共享工具:异构系统下,主备机切换时可能需要共享Context。可以使用Context共享工具来共享主备的Context。
  • 注解式编程,极简的使用方式:我们追求的是极简的、用户编程体验良好的编程方式。通过简单的标注+极简单的代码撰写,即可完成复杂的配置分布式化。
  • 需要Spring编程环境

它的设计理念是:

  • 简单,用户体验良好:
    • 摒弃了打散化配置的管理方式[2,3],仍旧采用基于配置文件的编程方式,这和程序员以前的编程习惯(配置都是放在配置文件里)一致。特别的,为了支持较为小众的打散化配置功能,还特别支持了配置项。
    • 采用了基于XML无代码侵入编程方式:只需要几行XML配置,即可实现配置文件发布更新统一化、自动化。
    • 采用了基于注解式的弱代码侵入编程方式:通过编程规范,一个配置文件一个配置类,代码结构简单易懂。XML几乎没有任何更改,与原springXML配置一样。真正编程时,几乎感觉不到配置已经分布式化
  • 可以托管任何类型的配置文件,这与[2,3]只能支持KV结构的功能有较大的改进。
  • 配置更新实时推送
  • 提供界面良好Web管理功能,可以非常方便的查看配置被哪些实例使用了。

详细设计

架构设计

disconf服务集群模式

disconf的模块架构图

每个模块的简单介绍如下:

  • Disconf-core
    • 分布式通知模块:支持配置更新的实时化通知
    • 路径管理模块:统一管理内部配置路径URL
  • Disconf-client
    • 配置仓库容器模块:统一管理用户实例中本地配置文件和配置项的内存数据存储
    • 配置reload模块:监控本地配置文件的变动,并自动reload到指定bean
    • 扫描模块:支持扫描所有disconf注解的类和域
    • 下载模块:restful风格的下载配置文件和配置项
    • watch模块:监控远程配置文件和配置项的变化
    • 主备分配模块:主备竞争结束后,统一管理主备分配与主备监控控制
    • 主备竞争模块:支持分布式环境下的主备竞争
  • Disconf-web
    • 配置存储模块:管理所有配置的存储和读取
    • 配置管理模块:支持配置的上传、下载、更新
    • 通知模块:当配置更新后,实时通知使用这些配置的所有实例
    • 配置自检监控模块:自动定时校验实例本地配置与中心配置是否一致
    • 权限控制:web的简单权限控制
  • Disconf-tools
    • context共享模块:提供多实例间context的共享。

流程设计

运行流程详细介绍:

与2.0版本的主要区别是支持了:主备分配功能/主备切换事件。

  • 启动事件A:以下按顺序发生。
    • A3:扫描静态注解类数据,并注入到配置仓库里。
    • A4+A2:根据仓库里的配置文件、配置项,去 disconf-web 平台里下载配置数据。这里会有主备竞争
    • A5:将下载得到的配置数据值注入到仓库里。
    • A6:根据仓库里的配置文件、配置项,去ZK上监控结点。
    • A7+A2:根据XML配置定义,到 disconf-web 平台里下载配置文件,放在仓库里,并监控ZK结点。这里会有主备竞争。
    • A8:A1-A6均是处理静态类数据。A7是处理动态类数据,包括:实例化配置的回调函数类;将配置的值注入到配置实体里。
  • 更新配置事件B:以下按顺序发生。
    • B1:管理员在 Disconf-web 平台上更新配置。
    • B2:Disconf-web 平台发送配置更新消息给ZK指定的结点。
    • B3:ZK通知 Disconf-cient 模块。
    • B4:与A4一样。
    • B5:与A5一样。
    • B6:基本与A4一样,唯一的区别是,这里还会将配置的新值注入到配置实体里。
  • 主备机切换事件C:以下按顺序发生。
    • C1:发生主机挂机事件。
    • C2:ZK通知所有被影响到的备机。
    • C4:与A2一样。
    • C5:与A4一样。
    • C6:与A5一样。
    • C7:与A6一样。

模块实现

disconf-web提供了前后端分离的web架构,具体可见:https://github.com/knightliao/disconf/tree/master/disconf-web

本部分会重点介绍disconf-client的实现方式。

注解式disconf实现

本实现会涉及到 配置仓库容器模块、扫描模块、下载模块、watch模块,

http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg

使用AOP拦截的一个好处是可以比较轻松的实现配置控制,比如并发环境下的配置统一生效。关于这方面的讨论可以见这里

特别地,本方式提供的编程模式非常简单,例如使用以下配置类的程序在使用它时,可以直接@Autowired进来进行调用,使用它时就和平常使用普通的JavaBean一样,但其实它已经分布式化了。配置更新时,配置类亦会自动更新。

@Service
@DisconfFile(filename = "redis.properties")
public class JedisConfig {

    // 代表连接地址
    private String host;

    // 代表连接port
    private int port;

    /**
     * 地址, 分布式文件配置
     * 
     * @return
     */
    @DisconfFileItem(name = "redis.host", associateField = "host")
    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    /**
     * 端口, 分布式文件配置
     * 
     * @return
     */
    @DisconfFileItem(name = "redis.port", associateField = "port")
    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

基于XML配置disconf实现

本实现提供了无任何代码侵入方式的分布式配置。

ReloadablePropertiesFactoryBean继承了Spring Properties文件的PropertiesFactoryBean类,管理所有当配置更新时要进行reload的配置文件。对于被管理的每一个配置文件,都会通过 配置仓库容器模块、扫描模块、下载模块、watch模块 进行配置获取至配置仓库里。

ReloadingPropertyPlaceholderConfigurer继承了Spring Bean配置值控制类PropertyPlaceholderConfigurer。在第一次扫描spring bean里,disconf会记录配置文件的配置与哪些bean有关联。

ReloadConfigurationMonitor是一个定时任务,定时check本地配置文件是否有更新。

当配置中心的配置被更新时,配置文件会被下载至实例本地,ReloadConfigurationMonitor即会监控到此行为,并且通知 ReloadingPropertyPlaceholderConfigurer 对相关的bean类进行值更新。

特别的,此种方式无法解决并发情况下配置统一生效的问题。

主备分配实现

在实现中,为每个配置提供主备选择的概念。用户实例在获取配置前需要先进行全局唯一性竞争才能得到配置值。在这里,我们采用基于zookeeper的全局唯一性锁来实现。

Comparisons

  淘宝Diamond[2] Disconf 比较
数据持久性 存储在mysql上 存储在mysql上 都持久化到数据库里,都易于管理
推拉模型 拉模型,每隔15s拉一次全量数据 基于Zookeeper的推模型,实时推送 disconf基于分布式的Zookeeper来实时推送,在稳定性、实效性、易用性上均优于diamond
配置读写 支持实例对配置读写。支持某台实例写配置数据,并广播到其它实例上 只支持实例对配置读。通过在disconf-web上更新配置到达到广播写到所有应用实例 从目前的应用场景来看,实例对配置的写需求不是那么明显。disconf支持的中心化广播方案可能会与人性思考更加相似。
容灾 多级容灾模式,配置数据会dump在本地,避免中心服务挂机时无法使用 多级容灾模式,优先读取本地配置文件。 双方均支持在中心服务挂机时配置实例仍然可以使用
配置数据模型 只支持KV结构的数据,非配置文件模式 支持传统的配置文件模式(配置文件),亦支持KV结构数据(配置项) 使用配置文件的编程方式可能与程序员的编程习惯更为相似,更易于接受和使用。
编程模型 需要将配置文件拆成多个配置项,没有明显的编程模型 在使用配置文件的基础上,提供了注解式和基于XML的两种编程模型
并发性 多条配置要同时生效时,无法解决并发同时生效的问题




©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页