nacos配置中心
背景
如果只是简单的系统和应用也用不上配置中,当系统架构负载起来,为了方便的管理各个应用的配置,我们可以通过配置中心如nacos、apollo等,来实现一些配置变更、版本管理、灰度上线等功能;
作为配置中心,配置的实时变更应该是一个基础功能,那nacos是怎么实现他的呢
推还是拉
有这两种实现方式来实现实时变更:
推
由服务端发起,配置变更后一一推送给有注册过客户端,通过长链接可以保证推送的实时性,但是长链接可能会进入假死状态,即看着是连接着的,实际人早没了。所以还需要心跳机制来保证联通。
拉
拉(pull)模型,其实说白了就是客户端轮询,轮询也分长轮询和短轮询;
短轮询就是每隔一段时间查询一次。时间间隔设置太长,实时性会变差;设置过短,会影响客户端性能; 相对比长轮询就是一种更好的解决方案,长短说的不是轮询的间隔时间,而是轮询一次的时间;
长轮询,每次查询服务端,服务端如果有变更记录就直接返回,如果没有就hang住一段时间,如果在hang住的这段时间内,变更事件发生了,就会返回给客户端。这样在实时性和轮询次数上做了个平衡。
nacos的实时配置变更下发就是通过长轮询的方式做到的;
服务端代码解析
configController
客户端通过 /v1/cs/configs/listener 接口来发起一次长轮询。这个接口完成了两个工作,
查询客户端传进来的配置文件md5和服务端的配置文件md5是否一致,如果不一致则直接返回结果;
如果一致,则陷入长轮询状态,等待配置变更时间发生,直到超时时间;
那么我们关心的长轮询逻辑就能找到了在doPollingConfig方法中了。
这里判断header里是否有 Long-Pulling-Timeout ,如果没有则不是长轮询。
那么我们继续往 longPollingService.addLongPollingClient 里面走。
圈起来的第一部分是配置是否变更的double-check和超时时间的处理,这里就不赘述了;
直接看第二部分,通过获取 asyncConetext 将这次请求转为异步请求,然后讲这些参数一起塞到了ClientLongPolling 里面去了,看名字应该是长轮询的处理逻辑了。
获取asyncContext是servlet3.0的一个功能,通过asyncContext我们将耗时的任务放到工作线程中去,执行结束后在异步响应,这样我们就可以释放当前的servlet worker线程,提升我们服务端的吞吐量了。
AsyncContext asyncContext = request.startAsync(); //结束请求 返回响应 asyncContext.complete();复制代码
ClientLongPolling的功能就是等待到超时。超时之后自动返回;
长轮询的任务进来后,都会被塞到 allSubs 队列中,等待启动的这个定时任务delay时间已过,就先移除任务,然后再校验一遍配置变更,然后异步响应;
是不是很奇怪这个长轮询的流程都走没了,都没有看到哪里有配置实时推送的逻辑, 别着急仔细想想,我们怎样才能知道配置变更过了呢?
当然是在控制台变更或客户调用publish方法的时候,埋点记录一下,再通知给还在allSubs队列中客户啦;
那我们来找下这段逻辑吧
ConfigController.publishConfig
在配置发布接口中被我发现了, 看来是配置的变更被包装成了一个配置变更事件通知给用户
ConfigChangePublisher.notifyConfigChange(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime()));复制代码
然后我在 LongPollingService 的构造方法中看到了她注册了一个配置变更事件,当publish发生时,在ConfigChangePublisher 中注册过的事件就会执行了。
我们终于找到事件变更的主角了 DataChangeTask
这里面也就两步,
轮询allSubs队列中的客户端,
将和这次变更事件有影响的客户端,结束长轮询,响应出去
长轮询就此结束
总结
利用asyncContext释放servlet线程,保证吞吐量,提高效率;
通过ClientLongPolling等待,通过DataChangeTask取消等待,返回结果;
通过观察者模式事件通知,来实时捕获到配置的变更;
作者:我的头疼
链接:https://juejin.cn/post/7018168133257003022