尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Nacos 配置中心的长轮询监听机制与服务端推送实现

Nacos 配置中心的长轮询监听机制与服务端推送实现 Nacos 配置中心的长轮询监听机制与服务端推送实现在分布式微服务架构中配置中心扮演着集中式管理与动态热更新的核心角色。当研发人员在控制台修改了某个数据库连接池大小、熔断降级阈值或业务开关时集群内成百上千个微服务实例必须在毫秒至秒级内感知并完成内存更新。在技术选型上客户端获取配置变更通常有两种极端模型纯推模式Push服务端与每个客户端保持长连接有变更立即推送。缺点是长连接管理复杂若推送失败缺乏可靠的状态对齐机制。纯拉模式Pull客户端定时轮询如每隔 1 秒发一次 HTTP 请求。缺点是频繁空轮询对网络和服务端 CPU 造成巨大空耗而调大轮询间隔又牺牲了变更时效性。Nacos 配置中心在 1.x 时代采用了兼顾两者优势的HTTP 长轮询Long Polling机制并在 2.x 时代全面升级为gRPC 双向长连接流Bi-directional Stream推送。本文将深入剖析 Nacos 配置监听的底层实现原理。Nacos 1.x基于 HTTP 的长轮询Long Polling机制Nacos 1.x 并没有为每个客户端维持沉重的持久 TCP 长连接而是利用了 Servlet 3.0 的异步请求处理AsyncContext实现了高效的 HTTP 长轮询。[Nacos Client] [Nacos Server] │ │ ├────── 发起配置监听请求 (携带 DataIdGroupMD5) ───────┤ │ (超时时间 Timeout 设为 30s) │ │ ├─ 检查本地 MD5: │ │ 若已发生变更 ── 立即返回变更项 │ │ 若无变更 ────┐ │ │ │ 挂起请求 (AsyncContext) │ │ │ 最多等待 29.5 秒 │ │ │ │ ┌─────────────── 期间有配置发布 (Event) ──────────────┤────────────┘ │ │ │ (唤醒挂起的任务提前响应) │ ▼ │ │◄── 响应 200 OK (返回有变更的 DataId) ────────────────┤ │ │ ├────── 立即发起 GET 请求拉取最新配置完整内容 ───────────┤ │◄───── 返回最新配置字符串 ────────────────────────────┤ │ │ ├────── 重新发起下一轮 30s 长轮询 ──────────────────────┤1. 客户端监听任务与 MD5 摘要比对客户端在后台启动线程池按 3000 个配置为一个批次向服务端发送监听请求。请求体中携带当前客户端本地缓存的 MD5 摘要值。核心参数客户端 HTTP 请求超时时间设置为30 秒。服务端挂起时间设置为29.5 秒提前 500ms 响应防止网络延迟导致客户端直接抛出 SocketTimeoutException。2. 服务端异步挂起与事件驱动响应服务端接收到长轮询请求后通过 Spring 的AsyncContext释放 Tomcat 工作线程将请求包装为一个ClientLongPolling异步任务并提交到延迟调度池中// Nacos 1.x 服务端核心原理简化 public void doLongPolling(HttpServletRequest request, HttpServletResponse response, MapString, String clientMd5Map, int probeRequestSize) { // 1. 获取 Servlet 3.0 异步上下文 final AsyncContext asyncContext request.startAsync(); asyncContext.setTimeout(0L); // 禁用容器默认超时由 Nacos 内部调度器控制 // 2. 提交延迟任务默认 29.5s 后执行 ConfigExecutor.executeLongPolling( new ClientLongPolling(asyncContext, clientMd5Map, request, response, probeRequestSize) ); } class ClientLongPolling implements Runnable { // ... Override public void run() { // 延迟时间到达29.5s若期间无配置变更比对后返回空集合200 OK ListString changedGroupKeys MD5Util.compareMd5(request, response, clientMd5Map); if (!CollectionUtils.isEmpty(changedGroupKeys)) { generateResponse(changedGroupKeys); } else { generateResponse(null); } } // 当有配置被管理员发布修改时由 LocalDataChangeEvent 提前触发此方法 public void onEvent(LocalDataChangeEvent event) { // 匹配 groupKey若命中当前挂起客户端所关注的配置立即响应并结束 AsyncContext generateResponse(List.of(event.groupKey)); } }Nacos 2.x全面升级 gRPC 长连接多路复用随着微服务规模扩大到数万实例HTTP 长轮询在服务端产生的短连接握手开销以及大量的 HTTP Header 传输成本逐渐成为瓶颈。Nacos 2.0 引入了基于 Netty 的 gRPC 长连接架构。[Nacos 2.x Client] [Nacos 2.x Server] │ │ ├═══════ 建立单个 gRPC 双向长连接 (Port 9848) ═══════════┤ │ │ ├────── ConnectionSetupRequest (连接初始化注册) ────►│ ├────── ConfigBatchListenRequest (注册监听列表) ─────►│ │ │ │ [配置发布修改] │ │ │◄───── ConfigChangeNotifyRequest (精准 Push) ────┤ │────── ConfigChangeNotifyResponse (Ack 回执) ────►│ │ │ ├────── ConfigQueryRequest (拉取最新内容) ──────────►│ │◄───── ConfigQueryResponse (返回配置) ────────────┤1. gRPC 架构的核心收益连接多路复用单个微服务节点与 Nacos 服务端集群仅维持一条 TCP 长连接所有 DataId 的监听、心跳检测与配置拉取都在这条通道上复用 Stream极大地降低了网卡中断与端口占用。真正的毫秒级实时 Push配置发生变更时服务端直接通过 gRPC 通道向客户端发送ConfigChangeNotifyRequest帧端到端通知延迟从秒级缩短至 10~50 毫秒。状态同步与心跳保活基于 gRPC 的 Ping-Pong 机制实现 5 秒一次的健康探活。若连接断开客户端自动重连集群中的其他节点并在建连后立即发送全量监听配置清单进行对齐。客户端本地容灾与双层缓存设计在生产环境中外部配置中心可能遇到网络分区、集群宕机或机房断网。Nacos 客户端设计了严密的三层容灾降级体系[业务代码获取配置 (getConfig)] │ ▼ [1. 优先读取 Failover 本地容灾文件] (磁盘静态配置, 应急手工覆盖) │ (若不存在) ▼ [2. 读取本地快照 Snapshot 缓存文件] (每次从网络拉取成功后自动备份到磁盘) │ (若无本地快照) ▼ [3. 发起网络请求读取 Nacos 服务端最新配置]Snapshot 快照缓存每当客户端成功拉取一次远程配置都会将其同步写入本地磁盘目录默认路径~/.nacos/config/。若应用冷启动时恰逢 Nacos 集群不可用客户端将直接降级加载本地磁盘快照保证微服务依然能够成功拉起并对外提供服务。Failover 容灾机制当线上由于配置中心故障无法修改配置且服务面临重大生产事故时运维人员可以在服务器对应目录下放置同名的容灾配置文件并开启failovertrue客户端将直接跳过服务端与快照以该文件内容为最高优先级为紧急抢修争取宝贵时间。
返回列表