【技术挑战】Nacos自动刷新配置如何实现的?
由浏览前F12请求情况可知,更新配置,会调用 POST:/nacos/v1/cs/configs
API,具体的方法时 ConfigController#publishConfig
方法:
通过上述代码可知,修改配置后,服务端先通过 persistService.insertOrUpdate
将配置信息进行了更新,然后调用 EventDispatcher#fireEvent
触发一个 ConfigDataChangeEvent
事件。接下来,我们来查看一下fireEvent方法主要做了什么:
由此可见,fireEvent主要是根据事件class获取listener列表( CopyOnWriteArrayList
),然后循环调用每个listener的onEvent方法。而listener是通过 EventDispatcher#addEventListener
添加到listeners中。因此,我们只需找到调用 EventDispatcher#addEventListener
方法的地方,即可得知需要触发哪些 AbstractEventListener
的onEvent回调方法。
我们发现 AbstractEventListener
构造方法调用了 EventDispatcher.addEventListener(this)
方法,但是显然 AbstractEventListener
是抽象类,应找具体的实现类,很巧,一个熟悉的身影出现在面前: LongPollingService
。
所以,我们可以显而易见两条线路:
(1)在nacos控制台更改配置信息时 -> 调用 POST:/nacos/v1/cs/configs
API -> 触发 ConfigDataChangeEvent
事件 -> 调用listener的onEvent方法 -> LongPollingService#onEvent
(2)在nacos控制台更改配置信息时 -> 调用 POST:/nacos/v1/cs/configs
API -> 触发 ConfigDataChangeEvent
事件 -> 调用listener的onEvent方法 -> AsyncNotifyService#onEvent
那到底哪个是我们想要的呢?还记得我们触发的事件类型吗?没错,是 ConfigDataChangeEvent
事件,所以我们只需对比两条线路哪个是处理的该类型事件即可。
经过上述 LongPollingService#onEvent
和 AsyncNotifyService#onEvent
初步查看,基本可以确定 AsyncNotifyService
是我们想要的,但是事实真的是如此吗?我们通过查看 AsyncNotifyService.AsyncTask#executeAsyncInvoke
中并没有任何相关性。那么你可能会说, LongPollingService#onEvent
处理的是 LocalDataChangeEvent
岂不是更不相关?
然而答案却是 LongPollingService
,我们不要被表象所迷惑了。为什么呢?在Nacos中有一个DumpService,它会定时把变更后的数据dump到磁盘上。DumpService在Spring启动之后,会调用init方法启动几个dump任务,然后在任务执行结束之后,会触发一个LocalDataChangeEvent 的事件。
我们来看一下代码流转过程吧:
代码流程过程大致为: DumpService#init
-> DumpAllProcessor#process
-> ConfigService#dump
-> ConfigService#updateMd5
-> EventDispatcher.fireEvent(new LocalDataChangeEvent(groupKey))
所以,我们还是要会回到 LongPollingService#onEvent
中,其中会启动一个 DataChangeTask
线程,其中会有一个循环迭代器从 allSubs
里面获取 ClientLongPolling
对象,删除订阅关系后,调用 ClientLongPolling#sendResponse
将数据返回给客户端,这就是为什么配置信息可以实时触发更新的缘由了。
那如果 DataChangeTask
任务完成了数据返回客户端后, ClientLongPolling
中延迟任务开始执行怎么办?
haha~No Problem.因为在 DataChangeTask
调用 ClientLongPolling#sendResponse
返回数据给客户端时,会先取消超时任务,然后再反馈数据给客户端。代码如下: