如何实现配置在集群间同步的?
前提步骤
- 部署
MySQL
,并执行sql
脚本创建nacos schema
- 参照官网的
nacos
集群部署指南——nacos 集群部署指南 - 启动
nacos
集群
开始设置
- 在集群中任意登陆一台
nacos server
节点,然后创建配置 - 再次登陆其余节点,会发现配置信息已经同步到其余的节点中了
配置信息如何在集群间同步?
其实,这个配置信息同步是很简单的——直接从MySQL
中读取就好了,在集群模式下,nacos-server
需要使用数据库进行存储config
配置信息,然后其他nacos-server config
节点直接从数据库中读取即可
1 | /** |
CommunicationController
的作用
既然部署了nacos-server config
集群,那么nacos-client
连接的时候,基本上都是写上nacos-server cluster
集群下所有节点的IP
地址信息,那么这里就会有一个问题了,假设现在有三台nacos-server-config
,分别为nacos-A/nacos-B/nacos-C
,同时有一个nacos-client
,它连接到了nacos-A
,但是运维的同学,却在nacos-B
进行了配置发布,那么nacos-client
要怎么才能获取到最新的配置信息,换句话说,nacos-A
要怎么才能知道配置已经更新了?这个时候CommunicationController
的作用就体现出来了
CommunicationController
是用于其他节点通知的控制器,是被动接受配置更新通知的。那么,这个通知是怎么发出的呢?这个时候回到ConfigController.publishConfig
关键代码
1 | if (StringUtils.isBlank(betaIps)) { |
这里可以看到,在publishConfig
时,会发布一个ConfigDataChangeEvent
事件,然后这个事件,会被监听器AsyncNotifyService
接收
1 | // 异步通知任务,如果发生配置信息变更事件,将此事件广播给所有 nacos-server 集群 |
可以看到在循环语句中,创建了NotifySingleTask
这个任务,并且构造函数中的参数中,有一个入参为(String)ipList.get(i)
,而这个其实就是nacos-server config
集群中每个节点的IP
地址信息。然后再次创建一个AsyncTask
任务,去消费刚刚的Queue<NotifySingleTask>
核心方法
1 | private void executeAsyncInvoke() { |
然后会对nacos-server config
每个节点数据进行判断,符合条件的话,直接先nacos-server config
节点的/v1/cs/communication/dataChange
接口发起通知,告知有配置已更新;那如果说通知其他节点失败了呢?又该怎么做?这里其实nacos
都考虑到了,来看看AyscNotifyCallBack
这个回调函数
1 | class AyscNotifyCallBack implements FutureCallback<HttpResponse> { |
在回调函数的三个方法中,对于除成功以外的操作,都会将通知任务重新创建,放入executor
中等待重新执行
其他节点接收此更新事件的后续操作
在接收到更新事件通知后,CommunicationController.notifyConfigInfo
会执行下面的方法
1 | if (StringUtils.isNotBlank(isBetaStr) && trueStr.equals(isBetaStr)) { |
而最终,会进入到TaskManager
对象中,并且由DumpProcessor
进行处理;在DumpProcessor
的process
方法中,调用了一个重要的对象——ConfigService
。这个对象的所有数据更新操作,都会发布一个事件LocalDataChangeEvent
,而这个事件,在LongPollingService
处理,而这里存储的,是nacos-client
为了获取配置信息而发起的长链接的AsyncServlet
1 |
|
LongPollingService
会根据LocalDataChangeEvent
创建出一个DataChangeTask
任务,而这个对象,就是将nacos-client
感兴趣的配置信息主动告诉nacos-client
1 | public void run() { |
至此,链接了nacos-A
的nacos-client
,如果开发人员在nacos-B
上进行配置信息发布,也能够接受到最新的配置信息
代码流程图
DumpService为什么要dump出文件
其实并不是DumpService
执行了dump
文件的操作,该操作最终是由ConfigService
执行的,这里选择一个方法做展示
1 | static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs) { |
这里可以看到,在集群模式下,会选择将发布的配置信息进行一次dump
到文件的操作,而这dump
出的文件,最终被用于client
拉取配置文件时,直接从该文件中获取;
1 | // ConfigServletInner.doGetConfig |
之所以采用这个操作,而不是直接读MySQL
,是为了降低MySQL
的数据读取压力,因为在集群模式下,虽然推荐MySQL
采用高可用的部署,但是由于配置信息如果在发布时,可能导致大量的查询语句落在MySQL
上,因此采用本地文件缓存的形式,以降低MySQL
的数据读取压力