🏆【Alibaba中间件技术系列】「Nacos技术专题」配置中心加载原理和配置实时更新原理分析(中)

官方资源

https://nacos.io/zh-cn/docs/quick-start.html

带着问题去思考

  • 客户端长轮询的响应时间会受什么影响
  • 为什么更改了配置信息后客户端会立即得到响应
  • 客户端的超时时间为什么要设置为30s
  • 带着以上这些问题我们从服务端的代码中去探寻结论。

Nacos之配置中心

  • 动态配置管理是 Nacos的三大功能之一,通过动态配置服务,可以在所有环境中以集中和动态的方式管理所有应用程序或服务的配置信息。
  • 动态配置中心可以实现配置更新时无需重新部署应用程序和服务即可使相应的配置信息生效,这极大了增加了系统的运维能力。

动态配置

Nacos的动态配置的能力,看看 Nacos是如何以简单、优雅、高效的方式管理配置,实现配置的动态变更的,接下来来了解下 Nacos 的动态配置的功能。

客户端动态化配置机制

Nacos 的客户端维护了一个长轮询的任务,去检查服务端的配置信息是否发生变更,如果发生了变更,那么客户端会拿到变更的 groupKey 再根据 groupKey 去获取配置项的最新值即可。

客户端去发请求,询问服务端我所关注的配置项有没有发生变更,如果间隔时间设置的太长的话有可能无法及时获取服务端的变更,如果间隔时间设置的太短的话,那么频繁的请求对于服务端来说无疑也是一种负担。

如果客户端每隔一段长度适中的时间去服务端请求,而在这期间如果配置发生变更,服务端能够主动将变更后的结果推送给客户端,这样既能保证客户端能够实时感知到配置的变化,也降低了服务端的压力。

客户端长轮询

客户端长轮询的部分,也就是LongPollingRunnable中的checkUpdateDataIds 方法,该方法就是用来访问服务端的配置是否发生变更的,该方法最终会调用如下图所示的方法:

image
http请求操作

客户端是通过一个http的post 请求去获取服务端的结果的,并且设置了一个超时时间:30s。一般来讲:客户端足足等了29.5+s,才请求到服务端的结果,然后客户端得到服务端的结果之后,再做一些后续的操作,全部都执行完毕之后,在 finally 中又重新调用了自身,也就是说这个过程是一直循环下去的。

长轮询执行逻辑

客户端向服务端发起一次请求,最少要29.5s才能得到结果,当然啦,这是在配置没有发生变化的情况下。如果客户端在长轮询时配置发生变更的话,该请求需要多长时间才会返回呢,在客户端长轮询时修改配置。

未获得到修改数据的操作触发返回
image
获得到了修改数据操作立刻触发返回
image
服务端controller

上面说到了客户端发送的 http 请求中可以知道,请求的是服务端的 /v1/cs/configs/listener 这个接口,com.alibaba.nacos.config.server.controller.ConfigController.java,在 ConfigController 类中,如下图所示:


image

服务端是通过springMVC对外提供的 http 服务,对 HttpServletRequest 中的参数进行转换后,然后交给一个叫 inner 的对象去执行。inner 对象是 ConfigServletInner 类的实例,com.alibaba.nacos.config.server.controller.ConfigServletInner.java


image

该方法是一个轮询的接口,除了支持长轮询外还支持短轮询的逻辑。再次进入 longPollingService 的 addLongPollingClient 方法,如下图所示:

com.alibaba.nacos.config.server.service.LongPollingService.java

image

该方法主要是将客户端的长轮询请求添加到某个东西中去:服务端将客户端的长轮询请求封装成一个叫 ClientLongPolling 的任务,交给 scheduler 去执行。

服务端拿到客户端提交的超时时间后,又减去了 500ms 也就是说服务端在这里使用了一个比客户端提交的时间少 500ms 的超时时间,也就是 29.5s,看到这个 29.5s 我们应该有点兴奋了。

PS:这里的 timeout 不一定一直是 29.5,当 isFixedPolling() 方法为 true 时,timeout 将会是一个固定的间隔时间,这里为了描述简单就直接用 29.5 来进行说明。

接下来我们来看服务端封装的 ClientLongPolling 的任务到底执行的什么操作,如下图所示:

com.alibaba.nacos.config.server.service.LongPollingService.ClientLongPolling.java

image

ClientLongPolling 被提交给 scheduler 执行之后,实际执行的内容可以拆分成以下四个步骤:

  1. 创建一个调度的任务,调度的延时时间为 29.5s。
  2. 将该 ClientLongPolling 自身的实例添加到一个 allSubs 中去。
  3. 延时时间到了之后,首先将该 ClientLongPolling 自身的实例从 allSubs 中移除。
  4. 获取服务端中保存的对应客户端请求的 groupKeys 是否发生变更,将结果写入 response 返回给客户端。
image

allSubs 对象,该对象是一个 ConcurrentLinkedQueue 队列,ClientLongPolling 将自身添加到队列中。

调度任务

服务端对客户端提交上来的 groupKey 进行检查,如果发现某一个 groupKey 的 md5 值还不是最新的,则说明客户端的配置项还没发生变更,所以将该 groupKey 放到一个 changedGroupKeys 列表中,最后将该 changedGroupKeys 返回给客户端。对于客户端来说,只要拿到 changedGroupKeys 即可。

服务端数据变更

服务端直到调度任务的延时时间到了之前,ClientLongPolling 都不会有其他的任务可做,所以在这段时间内,该 allSubs 队列肯定有事情需要进行处理。

在客户端长轮询期间,更改了配置之后,客户端能够立即得到响应。

服务端数据变更接口

调用的请求,可以很容易的找到该请求对应的 url为:/v1/cs/configs 并且是一个 POST 请求,具体的方法是 ConfigController 中的 publishConfig 方法,如下图所示:

image

修改配置后,服务端首先将配置的值进行了持久化层的更新,然后触发了一个 ConfigDataChangeEvent 的事件,fireEvent 的方法:

com.alibaba.nacos.config.server.utils.event.EventDispatcher.java

image

fireEvent 方法实际上是触发的 AbstractEventListener 的 onEvent 方法,而所有的 listener 是保存在一个叫 listeners 对象中的。

image

被触发的 AbstractEventListener 对象则是通过 addEventListener 方法添加到 listeners 中的,找到 addEventListener 方法在何处被调用的,就知道有哪些 AbstractEventListener 需要被触发 onEvent 回调方法了。

可以找到是在 AbstractEventListener 类的构造方法中,将自身注册进去了,如下图所示:

com.alibaba.nacos.config.server.utils.event.EventDispatcher.AbstractEventListener.java

image

可以看到 AbstractEventListener 所有的子类中LongPollingService。当我们从 dashboard 中更新了配置项之后,实际会调用到 LongPollingService 的 onEvent 方法。

回到 LongPollingService 中,查看一下 onEvent 方法,如下图所示:

image

com.alibaba.nacos.config.server.service.LongPollingService.DataChangeTask.java

发现当触发了 LongPollingService 的 onEvent 方法时,实际是执行了一个叫 DataChangeTask 的任务,应该是通过该任务来通知客户端服务端的数据已经发生了变更,我们进入 DataChangeTask 中看下具体的代码,如下图所示:

image
遍历 allSubs 的队列

遍历 allSubs 的队列,该队列中维持的是所有客户端的请求任务,需要找到与当前发生变更的配置项的 groupKey 相等的 ClientLongPolling 任务

往客户端写响应数据

丢i与ClientLongPolling 任务后,只需要将发生变更的 groupKey 通过该 ClientLongPolling 写入到响应对象中,就完成了一次数据变更的 “推送” 操作了

如果 DataChangeTask 任务完成了数据的 “推送” 之后,需要将原来等待执行的调度任务取消掉了,这样就防止了推送操作写完响应数据之后,调度任务又去写响应数据。

可以从 sendResponse 方法中看到,确实是这样做的:

image

http请求本来就是无状态的,所以没必要也不能将超时时间设置的太长,这样是对资源的一种浪费。

image

与此同时服务端也将该请求封装成一个调度任务去执行,等待调度的期间就是等待 DataChangeTask 主动触发的,如果延迟时间到了 DataChangeTask 还未触发的话,则调度任务开始执行数据变更的检查,然后将检查的结果写入响应对象,如下图所示:

image

总结结论:

  1. Nacos 客户端会循环请求服务端变更的数据,并且超时时间设置为30s,当配置发生变化时,请求的响应会立即返回,否则会一直等到 29.5s+ 之后再返回响应
  2. Nacos 客户端能够实时感知到服务端配置发生了变化。
  3. 实时感知是建立在客户端拉和服务端“推”的基础上,但是这里的服务端“推”需要打上引号,因为服务端和客户端直接本质上还是通过 http 进行数据通讯的,之所以有“推”的感觉,是因为服务端主动将变更后的数据通过 http 的 response 对象提前写入了。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容