跳到主要内容
📖 本章预览

本章为预览版本,展示部分核心内容。完整内容包含详细源码解析、实战代码和面试要点,加入知识星球即可解锁全部章节。

第四章:通信层 — gRPC 长连接架构

上一章我们跟踪了服务注册的全链路,其中 gRPC 通信层是请求从客户端到达服务端的"高速公路"。本章深入拆解这条高速公路的设计与实现。

gRPC双Server通信架构

4.1 为什么从 HTTP 切换到 gRPC

Nacos 1.x 的通信架构有三个致命问题:

场景1.x 方案问题
配置监听HTTP 长轮询(hold 住 30s)Tomcat 线程池被大量 hold 住的连接占满
服务推送UDP 推送不可靠,丢包无感知,客户端需要兜底轮询
每次请求HTTP 短连接频繁建连/断连,TCP 三次握手开销大

Nacos 2.x 的 gRPC 长连接方案:

  • 一条连接复用所有通信:注册、注销、订阅、配置监听、心跳、推送,全部走同一条 gRPC 连接
  • 双向流:服务端可以随时通过已建立的连接向客户端推送消息
  • HTTP/2 多路复用:一条 TCP 连接上并发多个请求,互不阻塞

4.2 双 gRPC Server 设计

Nacos 启动了两个 gRPC Server,端口不同,职责分离:

┌─────────────────────────────────────────────────┐
│ Nacos Server │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ GrpcSdkServer │ │ GrpcClusterServer │ │
│ │ 端口: 主端口+1000 │ │ 端口: 主端口+1001 │ │
│ │ │ │ │ │
│ │ 处理客户端请求: │ │ 处理集群内部通信: │ │
│ │ · 服务注册/注销 │ │ · Distro 数据同步 │ │
│ │ · 配置发布/查询 │ │ · Raft 日志复制 │ │
│ │ · 服务订阅 │ │ · 配置变更集群同步 │ │
│ │ · 配置监听 │ │ · 健康状态同步 │ │
│ │ · 心跳保活 │ │ │ │
│ └──────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────┘

为什么要分两个 Server?

  1. 安全隔离:SDK 端口对外暴露,Cluster 端口只在内网开放
  2. 流量隔离:大量客户端请求不会影响集群内部的数据同步
  3. 权限控制:通过 @InvokeSource 注解限制调用来源
// 只允许 SDK 调用的 Handler
@InvokeSource(source = "sdk")
public class InstanceRequestHandler extends RequestHandler<...> { }

// 只允许集群内部调用的 Handler
@InvokeSource(source = "cluster")
public class DistroDataRequestHandler extends RequestHandler<...> { }

4.3 ConnectionManager — 连接池管理器(深度源码分析)

4.3.1 核心数据结构

public class ConnectionManager {
// 所有活跃连接:connectionId → Connection
Map<String, Connection> connections = new ConcurrentHashMap<>();

// 连接事件监听器注册中心
private final ClientConnectionEventListenerRegistry connectionEventListenerRegistry;
}

4.3.2 连接注册 — 建连时发生了什么

public synchronized boolean register(String connectionId, Connection connection) {
// 第一步:连接数限制检查(DDoS 防护)
if (checkLimit(connection)) {
return false;
}

// 第二步:存入连接池
connections.put(connectionId, connection);

// 第三步:通知所有 ClientConnectionEventListener(观察者模式)
connectionEventListenerRegistry.notifyClientConnected(connection);
// naming 模块收到通知 → 创建 Client 对象
// config 模块收到通知 → 初始化配置监听上下文

return true;
}

设计亮点register 加了 synchronized,保证连接注册的原子性。但 getConnection 等读操作直接读 ConcurrentHashMap,不需要加锁。写安全 + 读高效的平衡。


🔒 解锁完整内容

本章剩余内容需要解锁后查看

以上仅为本章部分预览内容,完整内容包含更多深度源码解析、实战代码和面试要点。

加入知识星球你将获得:

  • ✅ 全部 17 章完整内容 + 持续更新
  • ✅ 配套源码 + 实战项目
  • ✅ 一对一答疑 + 面试辅导
  • ✅ 简历优化 + 内推机会

📚 本章完整目录

以下为本章完整目录结构,加入知识星球即可解锁全部内容。

4.3.3 连接注销 — 断连时的连锁反应

4.3.4 连接保活与不活跃剔除

4.4 RequestHandlerRegistry — 处理器自动注册(深度源码分析)

4.5 RpcPushService — 服务端主动推送

4.6 面试热点