Nacos 2.0 引入了基于 gRPC 的长连接通信模型,彻底重构了客户端与服务端的通信方式,取代了 1.x 版本中基于 HTTP 短连接 + 轮询 + HTTP Push 的模式。这一变革带来了 更低延迟、更高吞吐、更可靠的配置推送与服务发现能力。
本文将从 源码角度 深入解析 Nacos 2.0 的 gRPC 推送模式,涵盖:
- gRPC 长连接的建立
- 客户端注册与连接管理
- 服务端如何通过 Stream 推送配置变更
- 客户端接收与响应
- 心跳与连接保活
- 故障恢复机制
一、gRPC 推送的核心优势(对比 Nacos 1.x)
| 特性 | Nacos 1.x(HTTP) | Nacos 2.0(gRPC) |
|---|---|---|
| 通信协议 | HTTP 短连接 | gRPC 长连接(基于 HTTP/2) |
| 配置推送 | HTTP Push(不可靠) | Stream 流式推送(可靠) |
| 服务发现 | 客户端轮询 | 服务端主动通知 |
| 连接管理 | 无状态 | 有状态连接(Connection) |
| 性能 | 高延迟、高资源消耗 | 低延迟、高并发、低开销 |
二、核心模块与源码包结构
com.alibaba.nacos.core.remote
├── grpc.GrpcConnection // gRPC 连接封装
├── grpc.GrpcServer // gRPC 服务端
├── grpc.BiRequestStreamObserver // 双向流观察者
├── service.RemoteConnectionService // 连接管理服务
└── controller.ConnectionController // 连接状态查询接口
com.alibaba.nacos.naming.remote.grpc
├── NamingGrpcService // 服务发现 gRPC 服务
└── NamingPushService // 服务端推送实现
com.alibaba.nacos.config.remote.grpc
├── ConfigGrpcService // 配置中心 gRPC 服务
└── ConfigPushService // 配置推送实现
三、gRPC 长连接建立流程
1. 客户端启动:GrpcClient
// com.alibaba.nacos.common.remote.client.grpc.GrpcClient
public class GrpcClient extends Client {
@Override
public void connectToServer(ServerInfo serverInfo) {
// 构建 gRPC Channel
ManagedChannel channel = NettyChannelBuilder
.forAddress(serverInfo.getIp(), serverInfo.getGrpcPort())
.usePlaintext() // 默认不加密
.keepAliveTime(30, TimeUnit.SECONDS)
.build();
// 创建双向流 Stub
RemoteServiceGrpc.RemoteServiceStub stub = RemoteServiceGrpc.newStub(channel);
// 建立流式连接
StreamObserver<Request> requestObserver = stub.requestStreaming(new ResponseStreamObserver());
// 发送客户端注册请求
RegisterRequest registerRequest = new RegisterRequest();
registerRequest.setClientID(clientId);
registerRequest.setLabels(getLabels()); // 包含角色:config/naming
requestObserver.onNext(registerRequest);
}
}
- 使用
NettyChannelBuilder建立 gRPC 连接(默认端口9848)。 - 调用
requestStreaming建立 双向流(Bidirectional Stream)。 - 发送
RegisterRequest注册客户端身份。
2. 服务端接收连接:BiRequestStreamObserver
// com.alibaba.nacos/core/remote/grpc/BiRequestStreamObserver.java
public class BiRequestStreamObserver implements StreamObserver<Request> {
private final GrpcConnection connection;
@Override
public void onNext(Request request) {
if (request instanceof RegisterRequest) {
handleRegister((RegisterRequest) request);
} else {
// 处理其他请求
RequestHandler handler = requestHandlerRegistry.findHandler(request);
handler.handle(request, connection);
}
}
private void handleRegister(RegisterRequest request) {
String clientId = request.getClientID();
String role = request.getLabels().get("role"); // 如: ConfigRole, NamingRole
// 创建连接对象
connection = new GrpcConnection(channel, connectionId, request.getIp(), request.getLabels());
// 注册到连接管理器
remoteConnectionService.registerConnection(connection);
// 根据角色初始化处理器
if ("ConfigRole".equals(role)) {
configGrpcService.addConnection(connection);
} else if ("NamingRole".equals(role)) {
namingGrpcService.addConnection(connection);
}
// 回复注册成功
Response response = Response.newBuilder().setRequestId(request.getRequestId()).setErrorCode(0).build();
connection.getResponseObserver().onNext(response);
}
}
- 服务端通过
BiRequestStreamObserver监听客户端消息流。 - 收到
RegisterRequest后:- 创建
GrpcConnection对象 - 注册到
RemoteConnectionService - 按角色绑定到
ConfigGrpcService或NamingGrpcService
- 创建
四、配置变更推送:ConfigPushService
1. 配置发布触发推送
// com.alibaba.nacos/config/server/service/ConfigServletInner.java
public boolean publishConfig(...) {
// ... 持久化配置
// 触发 gRPC 推送
NotifyCenter.publishEvent(new ConfigDataChangeEvent(...));
}
2. ConfigPushService 监听事件
// com.alibaba.nacos/config/remote/grpc/ConfigPushService.java
@EventListener
public void onConfigChange(ConfigDataChangeEvent event) {
String dataId = event.getDataId();
String group = event.getGroup();
String tenant = event.getTenant();
// 获取所有订阅该配置的客户端连接
Set<Connection> connections = configConnectionManager.getConnections(dataId, group, tenant);
for (Connection conn : connections) {
// 构造推送消息
ConfigChangeNotifyRequest notifyRequest = new ConfigChangeNotifyRequest();
notifyRequest.setDataId(dataId);
notifyRequest.setGroup(group);
notifyRequest.setTenant(tenant);
notifyRequest.setNotificationId(event.getNotificationId());
// 通过 gRPC 连接推送
conn.asyncRequest(notifyRequest, new RequestCallBack() {
@Override
public void onResponse(Response response) {
Loggers.PUSH.info("Push ACK received from {}", conn.getMetaInfo());
}
@Override
public void onFail(Throwable e) {
// 推送失败,尝试重试或关闭连接
retryManager.addRetryTask(new RetryTask(notifyRequest, conn));
}
});
}
}
configConnectionManager维护了 dataId+group → Connection 列表 的映射。- 使用
conn.asyncRequest()通过 gRPC 流发送消息。 - 支持 ACK 回调,实现可靠推送。
五、客户端接收推送
1. 客户端 ResponseStreamObserver 处理消息
// com.alibaba.nacos/common/remote/client/grpc/ResponseStreamObserver.java
public class ResponseStreamObserver implements StreamObserver<Response> {
@Override
public void onNext(Response response) {
if (response instanceof ConfigChangeNotifyResponse) {
ConfigChangeNotifyRequest request = (ConfigChangeNotifyRequest) response.getRequest();
// 触发本地拉取最新配置
ConfigService.notifyListenConfig(request.getDataId(), request.getGroup(), request.getTenant());
}
}
}
- 客户端收到
ConfigChangeNotifyRequest。 - 调用
notifyListenConfig()主动从服务端拉取最新配置。
六、连接管理与心跳机制
1. 心跳保活:ConnectionProbeService
// com.alibaba.nacos/core/remote/ConnectionProbeService.java
@PostConstruct
public void start() {
GlobalExecutor.schedulePermit(this::probeAllConnections, 5, 5, TimeUnit.SECONDS);
}
private void probeAllConnections() {
for (Connection conn : connectionManager.allConnections()) {
if (conn.getLastActiveTime() < System.currentTimeMillis() - 30_000) {
// 发送心跳探测
conn.asyncRequest(new HealthCheckRequest(), resp -> {
if (resp.getErrorCode() != 0) {
closeAndClean(conn);
}
});
}
}
}
- 每 5 秒检查一次连接活跃性。
- 超过 30 秒无通信,发送
HealthCheckRequest探测。 - 失败则关闭连接并清理订阅关系。
2. 连接断开处理
// BiRequestStreamObserver.onCompleted() / onError()
@Override
public void onError(Throwable t) {
remoteConnectionService.unregisterConnection(connection);
configConnectionManager.removeConnection(connection);
namingConnectionManager.removeConnection(connection);
}
- 网络异常或客户端关闭时,自动清理连接和订阅关系。
七、服务端主动推送 vs 客户端拉取
| 阶段 | 方式 |
|---|---|
| 通知 | 服务端 → 客户端:gRPC Stream 推送 ConfigChangeNotifyRequest |
| 获取内容 | 客户端 → 服务端:HTTP GET /nacos/v1/cs/configs 拉取完整配置 |
| ACK 确认 | 客户端 → 服务端:gRPC 返回 Response |
✅ 这种“推拉结合”模式既保证了实时性,又避免了推送内容过大。
八、关键配置项
| 配置项 | 默认值 | 说明 |
|---|---|---|
nacos.server.grpc.port | 9848 | gRPC 服务端口 |
nacos.server.grpc.health.check.interval | 5s | 心跳检测间隔 |
nacos.remote.client.reconnect.delay | 2s | 客户端重连延迟 |
nacos.core.protocol.grpc.stream.max.inbound.message.size | 10MB | 最大消息大小 |
⚠️ Nacos 2.0 默认开启 gRPC,端口
9848和9849(TLS)必须开放。
九、源码调用链总结
客户端 GrpcClient.connect()
→ 发送 RegisterRequest
→ 服务端 BiRequestStreamObserver.onNext()
→ handleRegister()
→ 创建 GrpcConnection
→ 注册到 RemoteConnectionService
→ ConfigGrpcService.addConnection()
配置发布
→ ConfigServletInner.publishConfig()
→ NotifyCenter.publishEvent(ConfigDataChangeEvent)
→ ConfigPushService.onConfigChange()
→ configConnectionManager.getConnections()
→ conn.asyncRequest(ConfigChangeNotifyRequest)
→ 客户端 ResponseStreamObserver.onNext()
→ 触发本地拉取配置
十、总结
| 特性 | Nacos 2.0 gRPC 实现 |
|---|---|
| 通信模型 | 双向流长连接(gRPC Streaming) |
| 连接管理 | 有状态连接(GrpcConnection) |
| 推送机制 | 服务端主动推送变更通知 |
| 可靠性 | ACK + 重试 + 心跳检测 |
| 性能 | 高吞吐、低延迟、低资源占用 |
| 扩展性 | 支持多协议、多角色(Config/Naming) |
建议阅读源码路径
GrpcConnection.java—— 连接封装BiRequestStreamObserver.java—— 双向流处理ConfigPushService.java—— 配置推送RemoteConnectionService.java—— 连接管理GrpcClient.java—— 客户端实现
4558

被折叠的 条评论
为什么被折叠?



