写点什么

如何设计一个分布式配置中心?

  • 2025-01-06
    福建
  • 本文字数:3569 字

    阅读完需:约 12 分钟

1.面试官提问


面试官:假设你是公司的基础架构部门,现在需要设计内部的配置中心中间件,你要怎么设计?

我:设计客户端和服务端,客户端集成到业务项目中,项目启动时从服务端 pull 配置加载到本地,并且定时 check 服务端和本地配置是否一致,服务端如有更新,再 pull 到本地

面试官:那如果有几万台服务器,都是这样定时去 check,服务端压力岂不是很大,要怎么解决呢?

我:那改成用服务端 push 的方式???

面试官:......

面试官:那今天就到这里吧,你回去等通知吧......


2.为什么需要分布式配置中心


不了解底层原理的小卷只好回家后苦心专研分布式配置中心的原理,一定要弄清楚底层逻辑,下次要吊打面试官。


先来简单理解为什么需要配置中心?


我们开发的服务都是单体架构时,配置文件就和代码放在一起,如 springboot 的 application.yml 文件,对配置的修改只需要修改这一个文件就行。到分布式服务中,一个服务会有多台机器,不可能每个机器都单独修改配置文件,然后重新部署的。


这就要用到配置中心了,以 nacos 为例,下图是配置修改时和服务器间的操作:



3.开源框架


这里列举 4 种分布式配置中心的中间件,我们直接从一个中间件的原理来学习配置中心。


工作这么多年,应该得了解一些开源组件,大大小小的都行:


1、Apollo

2016 年 5 月,携程开源的配置管理中心,具备规范的权限、流程治理等特性。

GitHub 地址:https://github.com/apolloconfig/apollo


2、spring cloud config

2014 年 9 月开源,Spring Cloud 生态组件,可以和 Spring Cloud 体系无缝整合。


3、Nacos

2018 年 6 月,阿里开源的配置中心,也可以做 DNS 和 RPC 的服务发现。


4、Diamond

Diamond 出自淘宝,开源地址 【https://github.com/takeseem/diamond】 ,阿里集团内部的配置中心仍然用的 diamond,只是开源版本不再维护


面试时可能会问到为什么选择 Apollo 作为配置中心?不用其他的配置中心呢?


很多人用的时候就是看别人也这么用,或者大家都这么用,就选择了这个中间件。这里如果遇到了的话,就可以提到开源社区的活跃性,因为 Apollo 的社区生态活跃,且使用的公司特别多,常见的坑基本都被踩完了,所以选用 Apollo。


4. Apollo 工作原理


4.1 基础模型


Apollo 文档:Apollo配置中心设计,工作原理比较简单:


  1. 用户在配置中心对配置进行修改并发布

  2. 配置中心通知 Apollo 客户端有配置更新

  3. Apollo 客户端从配置中心拉取最新配置,更新本地配置并通知到应用



4.2 架构模块



解释下各个模块的功能:

  • Config Service 提供配置的读取、推送等功能,服务对象是 Apollo 客户端

  • Admin Service 提供配置的修改、发布功能,服务对象是 Apollo Portal(管理界面)

  • Config Service 和 Admin Service 都需要注册到 Eureka 并保持心跳;

  • Meta Server 是对 Eureka 做了一层封装,封装的是服务发现接口;

  • Client 通过域名访问 Meta Server 获取 Config Service 服务列表,即获取 IP+端口,然后通过 IP+端口访问服务,同时 Client 端自己做负载均衡,错误重试;

  • Portal 访问 Meta Server 获取 Admin Service 服务列表,也是获取 IP+端口,然后访问服务,Portal 侧也做负载均衡;


5. 使用 Apollo


官方有提供快速部署使用文档:Quick Start


具体操作步骤可以自行查看官方文档,这里我们主要通过简单使用 Apollo 来理解配置中心。部署完成后,登陆 Apollo 的管理界面,然后创建个应用,发布后再创建个配置,接着再次发布,如下图:



这里我是在本地启动的,访问 http://localhost:8080/可以查看已注册的实例



然后创建一个 Springboot 应用连接到 Apollo 配置中心,这里不写那么具体了,可以自行参考官方的Java客户端使用指南


Mac 电脑需先在本地的/opt/settings/server.properties文件中配置环境env=DEV,然后在application.properties文件中配置 Apollo 相关的内容如下:


# 接入Apollo配置app.id=multi_functionapollo.meta=http://localhost:8080# Apollo本地缓存路径apollo.cache-dir=/Users/longbig/log# 指定Apollo配置文件的环境env=DEV# 配置访问秘钥apollo.accesskey.secret=4c61a00512ad4cc09ef8a0e1ee672d89apollo.bootstrap.enabled=true
复制代码


为了测试客户端接收到配置中心配置变更的事件,我们参考官方文档的代码写个监听器的代码如下:


@Configuration@Slf4jpublic class ApolloConfig {    @Bean    public void init() {        Config config = ConfigService.getAppConfig();        config.addChangeListener(new ConfigChangeListener() {            @Override            public void onChange(ConfigChangeEvent changeEvent) {                log.info("Changes for namespace " + changeEvent.getNamespace());                for (String key : changeEvent.changedKeys()) {                    ConfigChange change = changeEvent.getChange(key);                    log.info(String.format("Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()));                }            }        });    }}
复制代码


最后测试验证,在管理界面增加一个配置,然后对配置修改发布,可以看到客户端已经接收到配置变更的事件了,并且打印出日志信息了


 


6. 配置发布后实时生效设计


从上面简单使用中可以看到,配置发布后实时推送到客户端。下面我们简要看一下这块是怎么设计实现的



配置发布的大致过程:

  1. 用户在 Portal 操作配置发布

  2. Portal 调用 Admin Service 的接口操作发布

  3. Admin Service 发布配置后,发送 ReleaseMessage 给各个 Config Service

  4. Config Service 收到 ReleaseMessage 后,通知对应的客户端


6.1 发送 ReleaseMessage 的实现方式


从上图看,应该是用 MQ 的方式比较合适,但是 Apollo 没有用外部消息中间件,而是通过数据库来实现这个简单的消息队列的。具体如下:


  1. Admin Service 在配置发布后会往 ReleaseMessage 表插入一条消息记录,消息内容就是配置发布的 AppId+Cluster+Namespace

  2. Config Service 有一个线程会每秒扫描一次 ReleaseMessage 表,看看是否有新的消息记录

  3. Config Service 如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener

  4. 消息监听器得到配置发布的 AppId+Cluster+Namespace 后,会通知对应的客户端



我们查看数据库的ReleaseMessageReleaseHistory表,可以查看到当前消息和历史消息


 


6.2 Config Service 通知客户端的实现方式


这里能解释说明开头的面试题,客户端更新配置是Pull还是Push的方式?


具体实现方式如下:


  1. 客户端会发起一个 Http 请求到 Config Service 的notifications/v2接口,也就是NotificationControllerV2

  2. NotificationControllerV2 不会立即返回结果,而是通过Spring DeferredResult把请求挂起

  3. 如果在 60 秒内没有该客户端关心的配置发布,那么会返回 Http 状态码 304 给客户端

  4. 如果有该客户端关心的配置发布,NotificationControllerV2 会调用 DeferredResult 的setResult方法,传入有配置变化的 namespace 信息,同时该请求会立即返回。客户端从返回的结果中获取到配置变化的 namespace 后,会立即请求 Config Service 获取该 namespace 的最新配置。


7. 客户端的工作原理



接着讲讲 Apollo 客户端的工作原理:

  1. 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过 Http Long Polling 实现)

  2. 客户端还会定时从 Apollo 配置中心服务端拉取应用的最新配置。这是一个 fallback 机制,为了防止推送机制失效导致配置不更新客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回 304 - Not Modified 定时频率默认为每 5 分钟拉取一次,客户端也可以通过在运行时指定 System Property: apollo.refreshInterval来覆盖,单位为分钟。

  3. 客户端从 Apollo 配置中心服务端获取到应用的最新配置后,会保存在内存中

  4. 客户端会把从服务端获取到的配置在本地文件系统缓存一份在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置

  5. 应用程序可以从 Apollo 客户端获取最新的配置、订阅配置更新通知


8.题外话


之前在第一家公司工作过程中,遇到个问题是:对应用某个配置的变更如何通知到生产环境的所有机器?

当时的场景是前端发起 HTTP 请求,调用后端接口修改配置,因为负载均衡的缘故,请求只会打到 1 台机器上,只有 1 台机器的内存配置被更新,其他机器的内存配置还是旧的,当时小组一起讨论解决办法,可能认知有限,只想到 MQ 等等方式,没想到配置中心的原理

后来去了阿里之后,参与过写配置中心配置变更监听器,实现了全量机器的内存配置更新功能

现在回想起来,当时没解决的原因还是认知不够,现在学了配置中心的原理又想到了这件事,分享给大家学习参考~


相信通过学习 Apollo 配置中心的原理,你在面试过程中如果遇到开头的题目,应该也能说上一二了。


文章转载自:卷福同学

原文链接:https://www.cnblogs.com/dnboy/p/18653198

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
如何设计一个分布式配置中心?_分布式_不在线第一只蜗牛_InfoQ写作社区