gRPC服务端必须显式配置KeepaliveParams,否则空闲连接会被中间件静默断开;需在grpc.NewServer()中设置MaxConnectionIdle、MaxConnectionAge等参数,并同步配置客户端KeepaliveParams及PermitWithoutStream=true。

gRPC服务端必须显式配置KeepaliveParams
Kratos底层用的是标准grpc.Server,它默认不启用任何心跳机制。空闲连接会在防火墙、NAT或LB中间件中被静默断开,导致客户端报connection closed before server preface received或transport is closing。必须在grpc.NewServer()中传入grpc.KeepaliveParams选项。
常见错误是只配了客户端keepalive,却漏掉服务端——服务端不响应心跳,客户端发再多也没用。
-
MaxConnectionIdle:空闲超时(如30 * time.Second),超时后主动关闭连接 -
MaxConnectionAge:强制重连周期(如10 * time.Minute),避免长连接老化累积问题 -
MaxConnectionAgeGrace:优雅关闭窗口(如5 * time.Minute),让正在处理的流自然结束 - 不要设
Time或Timeout为0,否则可能触发内核默认7200秒keepalive,完全失效
gRPC客户端需同步配置ClientParameters
服务端开了心跳,客户端不配对应参数,照样会因单向探测失败而断连。Kratos项目中通常在internal/client/grpc.go里构造grpc.Dial,必须加入grpc.WithKeepaliveParams。
注意PermitWithoutStream: true这个开关——没有活跃RPC调用时,客户端仍能发心跳;若为false,空闲期间心跳直接被抑制。
-
Time:客户端发送心跳间隔(建议≤服务端MaxConnectionIdle的一半) -
Timeout:等待服务端ACK的最大时长,太短易误判,太长拖慢故障感知 - 明文连接下务必配
grpc.WithTransportCredentials(insecure.NewCredentials()),否则keepalive握手失败
HTTP/2层keepalive和TCP keepalive不是一回事
很多人混淆grpc.KeepaliveParams(HTTP/2应用层心跳)和操作系统级SO_KEEPALIVE(TCP传输层保活)。前者由gRPC协议栈控制,可精细调节;后者由内核管理,粒度粗、延迟高,不能替代前者。
Kratos服务无需手动调用SetKeepAlive或改系统参数。只要gRPC两端KeepaliveParams配对合理,就能覆盖99%的连接漂移场景。
- HTTP/2心跳走DATA帧,带业务上下文,服务端可据此判断逻辑层存活
- TCP keepalive只验证链路层可达性,无法反映gRPC Server是否卡死或goroutine阻塞
- 两者可共存,但优先调优gRPC Keepalive,它是微服务心跳的事实标准
健康检查状态要和心跳联动
单纯心跳通不代表服务可用。Kratos不自动把keepalive成功映射到/health或gRPC Health服务状态。如果你用K8s readiness probe或服务网格做熔断,必须手动桥接。
推荐做法:在health.Checker实现里,把最近一次心跳响应时间、当前活跃连接数、关键依赖(DB/Redis)连通性一并纳入评估。
- 不要只依赖
grpc_health_v1的SERVING硬编码返回 - 心跳失败3次以上,应主动将
Health状态切为NOT_SERVING并触发反注册 - Watch接口流式推送状态变更时,别忘了加context超时,避免goroutine泄漏
实际生效的关键点往往藏在细节里:服务端MaxConnectionIdle比客户端Time小,会导致连接被服务端先关;PermitWithoutStream设错,会让空闲期彻底失联;而健康检查不联动心跳,就等于装了防盗门却没锁窗。


















