Nacos Config 的长轮询机制核心是客户端发起一个"挂起"的 HTTP 请求,服务端在配置变更或超时前不返回响应,以此实现低延迟和低资源消耗的动态刷新。其实现流程可分为客户端和服务端两部分,具体如下:
1. 客户端实现(主动发起长轮询)
客户端(如 Spring Boot 应用)通过 NacosConfigService 发起长轮询,核心逻辑是"发起请求→等待响应→处理结果→循环重试"。
Step 1:封装请求参数
markdown
客户端向服务端发送 HTTP GET 请求,携带关键参数:
dataId/group:指定要监听的配置标识。
tenant:命名空间(可选,用于多环境隔离)。
lastModified:客户端本地配置的最后修改时间(服务端以此判断配置是否变更)。
longPollingTimeout:长轮询超时时间(默认 30 秒,客户端等待响应的最长时间)。
Step 2:发起"挂起"请求
客户端通过 HTTP 客户端(如 OkHttp)发送请求,并设置较长的读取超时(略大于 longPollingTimeout,避免客户端提前断开连接)。此时请求处于"挂起"状态,客户端等待服务端响应。
Step 3:处理服务端响应
css
服务端返回响应后,客户端根据响应结果处理:
有配置变更:响应中携带变更的配置内容,客户端更新本地配置,并触发业务逻辑的刷新(如 Spring 的 @RefreshScope)。
无配置变更(超时):响应为空,客户端不做配置更新,立即发起下一次长轮询请求,重复整个流程。
核心保障
客户端通过线程池维护长轮询任务,即使某次请求失败(如网络波动),也会自动重试,确保监听不中断。
2. 服务端实现(被动响应+主动检测)
服务端的核心是接收请求后,先检测配置是否变更,若无变更则"挂起"请求,直到配置变更或超时再返回,避免频繁处理无效请求。
Step 1:接收并解析请求
arduino
服务端(Nacos Server)的 ConfigController 接收客户端请求,解析 dataId/group/lastModified 等参数,定位到对应的配置文件。
Step 2:立即检测配置是否变更
服务端对比客户端传入的 lastModified 与服务端配置的实际最后修改时间:
若已变更:直接返回变更后的配置内容,客户端可实时刷新。
若未变更:不立即返回响应,进入"挂起等待"流程。
Step 3:"挂起"请求,加入监听队列
bash
服务端将当前请求封装为一个 ClientLongPolling 任务,加入到配置监听队列(按 dataId/group 分组),同时启动一个超时定时器(时长 = longPollingTimeout)。此时请求被"挂起",不会占用服务端的工作线程(通过异步处理实现,避免线程阻塞)。
Step 4:触发响应的两种场景
当满足以下任一条件时,服务端会唤醒"挂起"的请求并返回响应:
1.配置主动变更:管理员在 Nacos 控制台修改配置并发布时,服务端会遍历该配置对应的监听队列,找到所有"挂起"的请求,立即返回变更后的配置。
2.请求超时:若配置在 longPollingTimeout 内未变更,超时定时器触发,服务端返回空响应,告知客户端"暂无变更"。
3. 关键技术支撑
-
异步处理:服务端使用线程池(如 EventExecutorGroup)处理长轮询任务,避免"挂起"请求阻塞核心业务线程。
-
内存监听队列:服务端维护按 dataId/group 分组的内存队列,确保配置变更时能快速定位到所有监听的客户端。
-
HTTP 协议兼容性:长轮询基于标准 HTTP 协议,无需额外建立长连接(如 WebSocket),兼容性更好,能穿透大部分防火墙。
总结
整个流程的本质是 "客户端主动问,服务端按需答" :客户端通过循环发起"挂起"请求保持监听,服务端仅在配置变更或超时后才回复,既保证了配置刷新的实时性(延迟≤超时时间),又大幅减少了无效请求(相比短轮询),同时避免了推送模式的长连接维护复杂度。