Alibaba Nacos配置中心动态感知原理示例解析
作者:Mr.Fire
引言
Nacos提供两大核心功能,服务注册发现,配置中心。对应Nacos的架构图,分别是Naming Service和Config Service,其中Config Service是实现配置中心的核心模块。实现了版本管理、灰度发布、监听管理、推送轨迹等功能。针对配置中心,当我们通过控制台或API修改配置之后,客户端能动态获取到修改后的配置,那么配置中心是如何实现动态感知的呢?
动态监听之Pull和Push
当Nacos Config Server上的配置发生变化时,需要让相关的应用程序感知到配置的变化,这就需要客户端对感兴趣的配置实现监听。那么客户端是如何实现配置变更实时更新的呢?
一般来说,客户端与服务端的交互无非两种:Pull模式和Push模式,一个是客户端主动拉取,一个是服务端主动推送。
- Pull模式:服务端和客户端之间需要维护长连接,客户端多的情况下耗内存、需要心跳机制检测连接状态。
- Push模式:客户端需要定时拉取数据,不能保证实时性,服务端长时间不更新的情况下,定时任务为无效更新,浪费资源。
Nacos的Pull模式
Nacos采用的是Pull模式,不过不是简单的Pull,而是一种长轮询机制。结合Pull和Push两者的优势,客户端采用长轮询的方式发起Pull请求,检查服务配置消息是否发生变化,如果更新,客户端会根据变更的内容获取最新配置信息。
所谓的长轮询,就是客户端发起Pull请求之后,服务端如果发生配置变更则立即返回,如果服务端和客户端配置是保持一致的,那么服务端会“Hold”住这个请求,在指定时间内不返回结果,直到这段时间内配置发生变化。这个长连接默认超时时间是30s。
服务端收到请求后,先检查配置是否发生变化,如果没变化,则设置一个定时任务,延期29.5s执行,并且把当前的客户端长轮询连接加入allSubs队列。这里有两种方式触发连接返回。
- 等待29.5s自动触发检查机制,无论是否发生变化,都会返回。
- 在29.5s内,通过Nacos控制台或者API的形式对配置进行了修改,会触发
ConfigDataChangeEvent
事件。
Nacos的动态感知
前面我们已经知道客户端通过长轮询请求来获取配置变更,但是定时任务是延迟执行的,那并没有达到实时的目的,当通过控制台或者API修改配置时,那Nacos是如何实时动态更新的呢?
LongPollingService 监听事件类
LongPollingService
继承AbstractEventListener
,AbstractEventListener
是事件抽象类,它有一个onEvent
抽象方法,而LongPollingService
实现了这个方法
@Override public void onEvent(Event event) { if (isFixedPolling()) { // ignore } else { if (event instanceof LocalDataChangeEvent) { LocalDataChangeEvent evt = (LocalDataChangeEvent)event; scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps)); } } }
LongPollingService
可以看到LocalDataChangeEvent
事件,这个事件是服务端的配置数据发生变化时发布的一个事件。onEvent
方法中通过线程池来执行一个DataChangeTask
任务。
DataChangeTask线程
DataChangeTask
是一个线程,实现了Runnable
接口,对应的run()
如下:
class DataChangeTask implements Runnable { @Override public void run() { try { ConfigService.getContentBetaMd5(groupKey); for (Iterator<ClientLongPolling> iter = allSubs.iterator(); iter.hasNext(); ) { ClientLongPolling clientSub = iter.next(); if (clientSub.clientMd5Map.containsKey(groupKey)) { // 如果beta发布且不在beta列表直接跳过 if (isBeta && !betaIps.contains(clientSub.ip)) { continue; } // 如果tag发布且不在tag列表直接跳过 if (StringUtils.isNotBlank(tag) && !tag.equals(clientSub.tag)) { continue; } getRetainIps().put(clientSub.ip, System.currentTimeMillis()); iter.remove(); // 删除订阅关系 LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}", (System.currentTimeMillis() - changeTime), "in-advance", RequestUtil.getRemoteIp((HttpServletRequest)clientSub.asyncContext.getRequest()), "polling", clientSub.clientMd5Map.size(), clientSub.probeRequestSize, groupKey); clientSub.sendResponse(Arrays.asList(groupKey)); } } } catch (Throwable t) { LogUtil.defaultLog.error("data change error:" + t.getMessage(), t.getCause()); } } DataChangeTask(String groupKey) { this(groupKey, false, null); } DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps) { this(groupKey, isBeta, betaIps, null); } DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps, String tag) { this.groupKey = groupKey; this.isBeta = isBeta; this.betaIps = betaIps; this.tag = tag; } final String groupKey; final long changeTime = System.currentTimeMillis(); final boolean isBeta; final List<String> betaIps; final String tag; }
- 遍历allSubs中的客户端长轮询请求。
- 比较每个客户端长轮询请求携带的groupKey,如果服务端变更的配置和客户端请求关注的配置一致,则直接返回,这里调用
clientSub.sendResponse()
方法返回。
总结
好了,最后整理下nacos实时动态感知的流程如下:
- 客户端通过长轮询的方式发起Pull请求服务端获取配置变更;
- 服务端判断如果是长轮询请求,对比数据的MD5,如果发生变化则直接返回,否则通过延迟任务执行
ClientLongPolling
线程; - 配置中心修改配置后,会发布
ConfigDataChangeEvent
事件; EventDispatcher
触发事件,通知监听者。LongPollingService
就是监听者之一。- 监听者通过线程池开启定时线程,遍历客户端的所有长轮询的请求, 通过groupKey匹配到对应请求,直接返回。
以上就是Alibaba Nacos配置中心动态感知原理示例解析的详细内容,更多关于Alibaba Nacos配置中心动态感知的资料请关注脚本之家其它相关文章!