gRPC C++ 客户端需显式配置 keepalive 参数(单位毫秒)并协同服务端设置,否则空闲连接易被中间设备关闭;关键参数包括 GRPC_ARG_KEEPALIVE_TIME_MS(≥30000)、GRPC_ARG_KEEPALIVE_TIMEOUT_MS(10000)、GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS(=1),服务端还需调高 GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA 并匹配保活间隔,同时需结合重试、重连与 stream 生命周期管理。

gRPC C++ 客户端默认不启用 keepalive,空闲连接在经过负载均衡器、NAT 设备或防火墙时极易被静默关闭,表现为后续调用突然报 rpc error: code = Unavailable desc = transport is closing。必须显式配置 GRPC_ARG_KEEPALIVE_TIME_MS 等参数,且服务端也需协同支持,否则客户端单方面发 PING 会触发 too_many_pings 错误并被 GOAWAY 终止连接。
keepalive 参数必须成对配置且单位是毫秒
C++ 中所有 keepalive 相关参数都通过 grpc::ChannelArguments 传入,不是布尔开关,也不是秒级单位 —— 这是最常见的误配点。错误写法如 args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 10)(以为是 10 秒),实际是 10 毫秒,会疯狂发 PING 导致服务端拒绝。
-
GRPC_ARG_KEEPALIVE_TIME_MS:空闲多少毫秒后开始发 PING,建议设为 30000(30 秒)以上,避开常见 LB 的 60 秒空闲超时 -
GRPC_ARG_KEEPALIVE_TIMEOUT_MS:等待 PING ACK 的毫秒数,建议 10000(10 秒),太短易误判断连,太长则故障发现延迟 -
GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS:设为 1(true)才允许无活跃 RPC 时发 PING;设为 0 则只在有 stream 或一元调用进行中才检测,对长轮询类场景无效
服务端不配合会导致 too_many_pings 和 GOAWAY
HTTP/2 的 PING 帧是双向的,但 gRPC 服务端默认可能禁用或限频。若客户端每 30 秒发一次 PING,而服务端未配置对应接收策略,前几次会被忽略,第 N 次后服务端会返回 GOAWAY 并附带错误码 too_many_pings,客户端日志里看到的是 transport closed 或直接崩溃。
- 服务端必须设置
GRPC_ARG_KEEPALIVE_TIME_MS(≥ 客户端值)和GRPC_ARG_KEEPALIVE_TIMEOUT_MS - 服务端还需设置
GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA(默认 2),若客户端保活间隔短、又无真实流量,需调高此值,例如设为 10 - 调试时抓包看是否有 HTTP/2 PING/PONG 帧交互,没有则说明某一方未生效
长连接断开后不能只靠 keepalive,必须叠加重试与重连
keepalive 只负责探测连接是否存活,不负责恢复。一旦收到 transport is closing 或 Unavailable,说明连接已不可用,此时仅重发请求会失败,必须重建 channel。
- 不要在每次 RPC 失败后重新
grpc::CreateChannel,开销大;应复用 channel,让底层自动重连(gRPC C++ 默认启用GRPC_ARG_HTTP2_RECONNECT_BACKOFF_MS) - 对关键流式调用(如双向 stream),需监听
grpc::ClientContext::AsyncNotifyWhenDone或检查Write/Read返回状态,在失败时主动Finish()并新建 stream - 若服务端重启频繁,建议客户端加指数退避重试 + channel 状态检查(
channel->GetState(true)),避免雪崩
keepalive 不是银弹:它解决的是“连接是否还通”,而不是“服务是否可用”。真正健壮的长连接逻辑,是 keepalive 探测 + channel 状态管理 + stream 生命周期控制 + 业务层重试策略四者组合,缺一不可。尤其注意 PermitWithoutCalls 和服务端 MaxPingsWithoutData 的匹配,这是线上最常被忽略的配置耦合点。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











