配置长轮询与推送
配置中心的关键不是“提供一个 GET 接口”,而是让大量客户端在没有变更时低成本等待,在发生变更时快速唤醒。Nacos 用 MD5 快照、异步请求和事件通知组合出这条路径。
先给答案:长轮询把“变化检测”从客户端空转变成服务端等待
Section titled “先给答案:长轮询把“变化检测”从客户端空转变成服务端等待”客户端发送配置摘要,服务端发现没有变化时不立即返回,而是保留请求并等待变更或超时。变更发生后服务端唤醒请求,客户端收到响应再拉取具体内容并更新本地缓存。这样节省的是无变化时的重复请求,不是把配置变成实时推送。
因此延迟排查要按顺序问:客户端是否监听了正确 dataId/group,服务端是否算出摘要变化,请求是否因超时或断线返回,客户端是否成功写入本地缓存,应用是否真的重新读取了新配置。任何一步卡住,都会出现“控制台已改但应用没变”。
ClientWorker cacheMap[groupKey -> CacheData] -> listen request + client MD5 -> LongPollingService#addLongPollingClient -> MD5 compare ├─ changed -> immediate response └─ same -> AsyncContext + ClientLongPolling ▲LocalDataChangeEvent ────┘ -> DataChangeTask -> response -> listener callback服务端先调用 MD5Util.compareMd5,如果发现变化立即返回;没有变化时使用 AsyncContext 挂起请求,并把轮询对象交给 ConfigExecutor。超时时间会预留提前返回窗口,避免客户端和服务端同时超时。
配置落盘或内存状态变化后,NotifyCenter 发布 LocalDataChangeEvent,订阅者提交 DataChangeTask,只唤醒匹配 group key 的等待请求。客户端 ClientWorker 将内容写入 CacheData,再在监听器线程中回调业务。
为什么不采用固定间隔轮询
Section titled “为什么不采用固定间隔轮询”替代方案:客户端每隔几秒执行一次普通查询。
为什么不行:无变更请求占用大量连接和 CPU,变更传播延迟又被轮询周期直接限制。
证据:addLongPollingClient 在 MD5 未变化时挂起异步请求;LocalDataChangeEvent 触发精准唤醒,形成“无变化少计算、有变化快通知”。
源码坐标索引
Section titled “源码坐标索引”LongPollingService#getSubscribleInfo—config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java:74LongPollingService#mergeSampleResult—config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java:111LongPollingService#addLongPollingClient—config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java:177LongPollingService#checkLimit—config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java:221LongPollingService#isSupportLongPolling—config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java:234NotifyCenter#registerSubscriber—config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java:251ClientWorker#addListeners—client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java:151ClientWorker#addTenantListeners—client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java:174ClientWorker$ConfigRpcTransportClient#notifyListenConfig—client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java:926ConfigExecutor#executeLongPolling—config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExecutor.java:130MD5Util#compareMd5—config/src/main/java/com/alibaba/nacos/config/server/utils/MD5Util.java:52
| 场景 | 现象 | 原因 | 规避 |
|---|---|---|---|
| 代理层超时 | 客户端频繁重连 | 网关 idle timeout 小于轮询窗口 | 统一代理、服务端和客户端超时 |
| 监听器阻塞 | 配置回调堆积 | 业务回调占用通知线程 | 回调中只做轻量更新,重活异步化 |
| 大量 group key | 内存和比较 CPU 上升 | 每个客户端维护缓存与 MD5 | 分组监听、限制单客户端订阅规模 |
长轮询是“拉模型连接保持”和“推模型事件唤醒”的折中。它适合穿越代理、兼容 HTTP 且变更频率中等的控制面;更高实时性可切换到长连接推送,但仍应保留版本校验和重连补偿。
面试锚点
- 长轮询和普通轮询的区别是什么?
- Nacos 如何避免配置变更通知丢失?
- 客户端本地
CacheData为什么不能只存内容?