日常开发中我们的应用中一般都会有数据库相关的配置,redis相关的配置,log4j相关的配置 等常用配置,这些我们称为静态配置,在应用启动的时候就需要加载,修改配置需要重启应用,这类配置一般是针对相关资源的访问地址和访问权限,还有一类配置和业务密切相关,应用在运行过程中需要监听这些配置的变化以方便修改运行模式或者响应对应的策略,例如并发控制数,业务开关等,可以用来做服务降级和限流,例如在数据库新老表做迁移的时候,我们可以用来配置进行动态切换模式:同步双写读老表,同步双写读新表,写新表读新表。如果这些配置不能进行集中式管理,那么当我们的服务部署有成千上万的实例后,即使借助ansible这些运维工具,那么修改配置也将是一件超级麻烦而且极容易出错的事情,在做发布的时候也不在敏捷。
所以,微服务架构下,我们需要一个集中式的配置管理系统,那么这个系统需要提供哪些功能才能解决上面的问题呢
1.权限控制,当然不能所有的人都可以修改配置,如果能够继承公司的SSO或者LDAP当然更好
2.审计日志,所有的修改需要记录操作日志,方便后续出现异议能够找到对应的操作人,也可以提供审批流程
3.环境管理,开发,测试,生成环境下的配置肯定要做隔离,相同group内的配置可能大多相同,例如同一个IDC机房的应用也可以有namespace做区分
4.配置回滚,当发现配置错误,或者在该配置下程序发生异常可以立即回滚到之前的版本,这需要该系统能够有版本管理的功能
5.灰度发布,有时候我们新上线一个功能,想先通过少部分流量测试下,这个时候我们可以随机只修改部分应用的配置,当测试正常后在推送到所有的应用
6.高可用,配置中心需要高可用,所以最好能支持集群部署,同时配置中心系统挂了之后最好能不影响应用,应用能够继续使用本地缓存的配置
7.配置中心,应该能够在配置发生变更后实时通知到应用,应用端可能是一个监听器,配置也可能就是一个普通bean里面的属性,需要自动监听到变化并进行调整
目前已经开源的集中式配置管理中间件里面有携程的apollo和百度的disconf,已经阿里的diamond和spring cloud生态下地config系统,下面我们主要分析下disconf和apollo的实现
disconf提供一个客户端程序集成在应用程序内方便应用去远程获取配置,并监听配置的变化,服务端提供配置的新增和修改,并给客户端提供获取配置的api
其中客户端有以下几个模块
scan模块
扫描所有的disconf注解,注解有两种,一张是标示这个bean是一个配置可以和具体的配置文件做管理,bean里面的属性就是配置文件里面的key-value,当服务端配置修改后,这个bean会自动通过反射调用对应属性的set方法,将这个bean里面属性的value值变更为最新的配置的值,另一个注解是标示这是一个配置监听器,当配置变更后,会调用监听器里的方法,当有业务需要关联到配置变化进行改变策略的时候可以加上这种注解
store模块
将从远程服务端拉取到的配置文件存储到本地,store模块有一个后台线程会定期扫描存储在本地的配置文件是否有变化,如果有变化就会通知scan模块扫描到得javabean和对应的监听器
fetch模块
当watch模块监控到节点变化后,通过http调用远程restful接口,获取最新配置,然后调用store模块存储
watch模块
监控zookeeper上node的变动,当有变化后会会调用fetch模块获取配置,服务端在做配置修改的时候会将ZOOKEEPER里面相关的临时节点删除,然后所有watch该node的应用都会收到通知,所有需要监听配置变化的应用都需要和zookeeper维护一个长连接
disconf的高可用保证:server端只是提供了配置的管理以及提供接口供客户端通过http获取, 所以server端是无状态的
相比于disconf长期没有维护,而且只提供了较为简单的分组管理功能,apollo社区目前比较活跃,而且功能更为完善,不仅仅提供了权限审计等安全措施,还提供了灰度发布,版本回滚等,在高可用方面将服务拆分了3个独立的模块,每个模块都可以独立集群化部署,也不用依赖zookeeper,而且有很多大公司做背书。不过disconf部署简单,而apollo就复杂的多
和应用端集成, 监听配置变化,获取最新配置
提供获取配置的接口供客户端使用以及当有新配置更新时会通知客户端,客户端可以以一定周期定时从ConfigService获取最新配置,configservice和客户端维护一个长连接,当有配置更新的时候,会通过长连接通知客户端去取最新配置
提供配置管理的接口供portal管理页面使用,例如配置的修改和发布 AdminService和ConfigService共用一个数据库,adminService每次发布配置的时候都会往releasemessage表里面插入一条记录,而configService会扫描这张表看是否有新插入数据,如果有变化就会通知客户端
提供登陆和审计日志的功能,提供http接口给管理界面,portal需要通过rpc调用AdminService提供的配置管理的接口,portal有自己的数据库
我们可以从两条线来看这个架构
是springcloud生态下一个服务注册和发现的插件,configService是集群部署的,而且是需要做负载均衡的,不能所有的客户端都和同一个configservice维护长连接,那么当客户端应用继续增长的时候,configservice也可以横向扩展来支撑,那么如何做到configservice在扩展的时候对应用无感呢,需要一个注册中心来做服务发现的功能,所以configservice会在启动和下线的时候通知注册中心,而客户端只需要去注册中心获取最新的configservice列表,让后选择一个就好了,而AdminService和potal之间存在rpc调用,而AdminService也是无状态并且集群化部署的,那么portal也可以通过注册中心去坐负载均衡,在其中一个AdminService节点挂了之后可以选其他的节点继续使用, 所以部署一套Eureka可以同时给configService和客户端,portal和adminService同时使用
这个服务主要是封装了Eureka的API,相当于Eureka的代理,这个服务主要是因为Eureka只有java的api做服务发现,如果应用要使用apollo便需要在客户端通过Eureka的java api获取adminservice的服务列表,而其他语言环境就没有办法了,所以MetaService提供了一个http接口供client和potal使用,客户端通过http请求MetaService就可以获取到AdminService得地址
由于上面的MetaService也是集群化部署的,那么client和portal怎么知道MetaService的地址呢,可以在nginx上配置一个域名,在nginx上做负载均衡转发到对应的metaservice, 而portal也是集群部署的,配置管理人员也需要通过域名访问nginx,在转发到对应的portal
所以portal节点和metaservice节点的增加和删除都需要在nginx上做对应的更改,而configService和AdminService则的弹性扩容则不需要