服务化体系之-配置中心,在ZK或etcd之外

 

如果一说到配置中心,你面前那个人立刻兴奋的和你讲ZooKeeper,etcd,很可能,你又遇到个空想型的玩家了,或者他家的系统其实很小。

 

1. 配置中心该有的清秀模样

 

家大业大的系统,肯定是要有个配置中心,统一管理所有应用的配置,并发布到对应的客户端上。

 

1. 推送: 将更改实时、准实时地推送到所有客户端。

 

2. 版本化与回滚:知道谁,什么时候,改了什么。最重要是,可以快速的回滚.....回滚、重启、灰度,可称运维三宝,在一时三刻便要报个P1故障的时候,一键回滚的便捷性如此重要。

 

3. 灰度发布:将更改推送到某些客户端上。同是运维三宝,先灰度一下能有效的降低风险。另外也是AB Test的一种实现方式 -10%机器配置成开关打开调用A系统,90%机器开关关闭走B系统。

 

4. 预案:先改好一些配置保存起来,但不下发。发生问题时,一键批量执行,如降级整条选购线所有服务的非关键功能,好过在兵荒马乱中颤抖着去修改。

 

5. 权限,审批,流程: 咳,咳。权限控制和Pair Check其实还是好的。

 

6. 同时支持 Web界面Restful API接口

 

7. 支持多语言,其中最头痛是支持php这种无状态的。

 

2. 客户端这边要怎么配衬呢?

1. 配合配置中心的实时/灰度推送,在参数变化时调用客户端自行实现的回调接口,不需要重启应用。

2. 支持环境变量,JVM启动参数,配置文件,配置中心等多种来源按优先级互相覆盖,并有接口暴露最后的参数选择。

3. 配置文件中支持多套profile,如开发,单元测试,集成测试,生产。

 


3. 现在可以来谈实现了

 

3.1 Netflix Archaius

被谈得最多的Netflix,其Archaius说白了只是个客户端,服务端是一点没有的。

基于Apache Commons Configuration Library的扩展,多层,实时/准实时的数据源包括了环境变量,配置文件,数据库,ZK,etcd等。但没有Spring多profile的支持。

 

3.2 Spring Cloud

其Config Server的实现相当奇特,基于git,没错,就是基于git,暴露成Restful API,所以也算是支持了版本化,但也仅此而已了,其他界面和功能什么的都需要自己另外做。

在客户端倒是集成了Config Server的读取,Spring本身也有profile机制。

而实时推送,还要自己拉上另一个Spring Cloud的Bus项目,基于RabbitMQ或Kafka来搭建一套推送体系,自己写代码实现。另外看文章,刷新时也不是通过回调接口,而是整个Bean重新构造,这对于有状态的Bean是不合适的。

 

3.3 国内开源

360开源了一个QCon,基于ZK,特色是基于Agent模式(见后)的多语言支持。但服务端也没有界面、灰度、预案什么的,直接通过API操作ZK而已。

淘宝的Diamond,有点历史了。

携程开源的Applo,支持Java,其他语言通过Http支持,看起来功能比较完整,界面模样周正,也还在更新维护。

还有个人开源的disconf,根脚在百度,只支持Java+Spring,看起来也可以只。

 

3.4 基于ZK或etcd的DIY

综上所述,最好的配置中心还是在各个互联网公司的基础架构部里,虽然改改改的过程可能很长,虽然现在都还不完美。

一般都会支持界面和API,用数据库来保存版本历史,预案,走流程(正儿八经的数据库表更方便查询,比如“你的待审批变更”)。最后下发到ZK或etcd这种有推送能力的存储里(服务注册本身也是用ZK或etcd,选型就一块了)。客户端都直接和ZK或etcd打交道。

灰度发布麻烦些,其中一种实现是同时发布一个可接收的IP列表,客户端监瑞脑消金兽听到配置节点变化时,对比一下自己是否属于该列表。

PHP这种无状态的语言和其他ZK/etcd不支持的语言,只好自己在客户端的机器上起一个Agent来监瑞脑消金兽听变化,再写到配置文件或Share Memory了。

我司走的就是这个路数。
 

最后,到底是ZK 还是etcd? 有些岁数的配置中心都是ZK的,但如果新写的话,的确可以考虑下etcd。虽然我没玩过,但看着它基于GRPC而支持多种语言,还可以动态修改集群,下发也不容易丢消息,挺不错。

 

3.5 基于运维自动化的配置文件推送

这是另一种玩法,一样有数据库与界面或API来管理配置,下发时生成配置文件,基于各种运维自动化工具如Puppet,Ansible推送到每个客户端。而应用则定时重新读取这个外部的配置文件。

 


4. 服务化框架的配置

 

除了业务上的值与开关的配置,服务化框架本身也需要配置。

 

服务化框架的配置又分两类:

  1. 框架底层的配置,如IO线程数,业务线程数,连接数,心跳间隔,日志配置等等。
  2. 每个服务/方法的元数据,如超时,重试,限流,熔断,路由,负载均衡等。

 

框架级的配置一般采用比较固定的配置方式,如配置文件,-D启动参数等,略过不谈。

 

4.1 服务元数据配置的演变

服务的配置,则有以下逐步提升的过程:

Day1, 根据配置产生作用的地方,服务端或客户端,各配各的。比如超时配在客户端的配置文件,限流配在服务端。

 

Day2, 考虑到有些客户端可能漏配或不懂要配什么,服务端也可以配置超时,并把配置上传到服务注册中心,作为客户端的默认值。很多开源出来的RPC框架,为了减少依赖也就只做到这一步了。

 

Day3,业务系统可能把其中一些配置,抽取到配置中心里面实现动态修改。

 

Day4,各个业务系统各搞各的服务配置,一个字,乱。那不如在配置中心里,搞一个专门针对服务的标准界面吧。有时候,这也叫做服务治理中心。

 

4.2 服务治理中心的模样

1. 以服务为维度,统一管理所有服务相关的配置,包括服务端的,所有客户端的配置。

同一个服务,不同的调用者,可能有不同超时要求,比如订单与商品详情两个服务,对库存服务的延时敏感度不一样。

甚至同一个调用者,比如订单服务,在查看订单和新增订单两个场景里,对库存服务的延时的敏感度会不同。

 

这么麻烦!!要不要还是回到每个客户端自己在配置文件或代码里设置超时算了?

 

我的建议,为了达致比较好的服务治理,还是应该把服务提供者与所有调用者的配置放在一起,而不是散布在代码里。作为一个服务提供者,如果完全不了解各个客户端在代码里设了什么超时,其实挺心虚的。

另外也可以做更多后续的治理工作,如整条调用链的超时设置展现。

同时,服务端获悉客户端那边的配置,也能够优化自己的处理,比如服务逻辑跑完就已经达到客户端那边配置的超时,那就不要序列化结果并发送回去了。 如果没有中央配置,客户端就要每次把超时时间也传输过去。

 

2. 多条件配置

除了前面提到的调用者/调用场景的不一样,可能还有服务在不同机房不同配置,灰度不同机器不同配置,服务默认值与某个方法特定值的差异等。

 

4.3 一种参考实现

需求很复杂,实现就要尽量简化.....无论界面怎么做,最后存储的JSON格式简化如下:

[

  //”订单服务“调用 “获取购物车方法” 为1000 

   {"method":"getCart","sourceApp":"orderService","value":1000},

  //默认值60

   {"value":60}

]

 

按顺序的多个规则,先中先走。规则里条件包括method, sourceApp, tag,targetIp等,多个条件是AND的关系。

而在读取配置时,不可能每次都做这样的解析,那就以空间换时间,把每一个遇到的method, sourceApp, targetIp,tag的组合的结果缓存起来。

 

好像所有开源的SOA框架里,只有我司的框架设计了这种多条件的配置。

 

我会的基础框架,细节对外说的不多。 这次抖漏了不少,怎么也值两块五了吧,赏么:)

版权声明:本文版权归  江南白衣@唯品会 所有。任何形式的转载,必须获得作者授权。

转载请保留原文链接: http://calvin1978.blogcn.com/articles/serviceconfig.html

最后,欢迎微信公众号jnby1978。

This entry was posted in 技术. Bookmark the permalink.

4 Responses to 服务化体系之-配置中心,在ZK或etcd之外

  1. liucyu says:

    能请教下博主你们公司是通过什么保证幂等性的吗?

  2. woxinyijiu says:

    https://github.com/ctripcorp/apollo ,楼主可以看看这个

  3. 匿名 says:

    感谢你,辛苦了

  4. 匿名 says:

    我推荐一个,XXL-CONF ,一个分布式配置管理平台,拥有"强一致性、毫秒级动态推送、多环境、多语言、配置监瑞脑消金兽听、权限控制、版本回滚"等特性。现已开放源代码,开箱即用。

    http://www.xuxueli.com/xxl-conf/

发表评论

您的电子邮箱不会被公开。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>