Kratos客户端不配Keepalive易断连,因gRPC协议栈无心跳、TCP SO_KEEPALIVE默认2小时远超中间件30–60秒空闲超时;必须两端显式配置KeepaliveParams且PermitWithoutStream=true,单边配置或默认false均导致空闲期连接被静默关闭。

Kratos客户端默认就支持连接复用,但必须显式配好grpc.WithKeepaliveParams和连接池参数,否则空闲连接大概率被中间件静默断开,报connection closed before server preface received或transport is closing。
为什么Kratos客户端不配Keepalive就容易断连
gRPC协议栈本身不发心跳,TCP层的SO_KEEPALIVE默认2小时才探测一次,远超NAT、防火墙、LB的空闲超时(通常30–60秒)。Kratos底层用的是标准grpc.Server和grpc.Dial,两端都得配,单边配置等于没配。
- 只配服务端
MaxConnectionIdle,客户端不发心跳 → 服务端等不到探测包,直接关闭连接 - 只配客户端
Time,服务端没开KeepaliveParams→ 心跳包无响应,客户端超时后主动断连 -
PermitWithoutStream设为false(默认值)→ 没有活跃RPC调用时,心跳被抑制,连接在空闲期直接失效
客户端grpc.Dial必须传的Keepalive参数
在internal/client/grpc.go或类似构造客户端的地方,grpc.Dial需明确加入以下选项:
-
grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: 15 * time.Second, Timeout: 10 * time.Second, PermitWithoutStream: true }):其中Time建议设为服务端MaxConnectionIdle的一半,确保心跳能赶在服务端关闭前抵达 - 必须搭配
grpc.WithTransportCredentials(insecure.NewCredentials())(明文)或credentials.NewTLS(...)(TLS),否则心跳握手失败 - 不要漏掉
grpc.WithConnectParams(grpc.ConnectParams{ MinConnectTimeout: 5 * time.Second }),避免建连阶段因网络抖动被快速放弃
连接池和并发参数怎么协同调优
Kratos客户端复用效果不仅取决于心跳,还受连接池行为影响。默认连接池会复用grpc.ClientConn,但若配置不当,仍可能频繁新建连接:
-
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(4 * 1024 * 1024))和grpc.MaxRecvMsgSize服务端参数要对齐,否则大消息触发重连 - 高并发场景下,若
MaxConcurrentStreams服务端设得太低(如默认100),客户端可能因流满而新建连接,建议设为1000以上 - 避免在每次RPC调用时都
grpc.Dial——必须全局复用同一个*grpc.ClientConn实例,否则连接池形同虚设
最容易被忽略的调试点
连接复用是否生效,不能只看日志有没有“connected”,要看真实连接生命周期:
- 用
netstat -an | grep :或ss -tnp | grep grpc确认客户端维持的连接数是否稳定,而非持续波动 - 服务端
grpc.KeepaliveParams中的MaxConnectionAgeGrace必须大于0,否则强制关闭时正在处理的流会被粗暴中断 - 如果用etcd或consul做服务发现,检查resolver是否在连接断开后及时触发重连,而不是卡在旧endpoint上死等
- Kratos v2.8.2起,
transport/grpc/transport.go暴露了grpc_client_conn_created_total等指标,可直接观测连接创建频次











