bigywater.tech
gRPC · 12

keepalive 参数:客户端与服务端的配置不匹配会怎样

keepalive 是 gRPC 在 HTTP/2 PING 帧上做的连接探活。它怎么设、有哪些隐藏的下限、客户端和服务端的配置不匹配会怎样、对端悄悄消失时能多快发现?这篇用本机服务端和一个能丢弃数据的代理逐项实测。

10 秒
客户端 keepalive.Time 的下限(设成 200 ms 也会被抬到 10 秒)
第 30.0 秒
服务端默认策略下,客户端的第三个 PING 之后收到 GOAWAY too_many_pings
12.0 秒 vs 30.0 秒
对端变黑洞后,有 keepalive 与没有 keepalive 的调用返回时刻

keepalive 是什么

实测 · 本机执行输出
  客户端 PING 的时刻 [9.9 19.9 30.0] 秒;服务端 ACK 的时刻 [9.9 19.9 30.0] 秒
  30.0 秒 S→C GOAWAY:最后流 1,错误码 ENHANCE_YOUR_CALM,附加数据 "too_many_pings"

用 HTTP/2 PING 帧探测连接是否还活着:隔一段时间(Time)发一个 PING,如果在 Timeout 内没有收到 ACK 就认为连接已死并关掉。客户端和服务端各自都可以发。

客户端 Time 的下限

实测 · 本机执行输出
  客户端 PING 的时刻 [9.9 19.9 30.0 40.0] 秒;服务端 ACK 的时刻 [9.9 19.9 30.0 40.0] 秒;GOAWAY 0 个;TCP 连接 1 条

grpc-go 里客户端 Time 小于 10 秒会被抬到 10 秒(源码),服务端的下限是 1 秒。实验里设 200 ms,客户端 PING 间隔仍是 10 秒;如果服务端用默认的 EnforcementPolicy,第三个 PING 之后就被 GOAWAY(ENHANCE_YOUR_CALM,too_many_pings)。

两端要配套

实测 · 本机执行输出
  有 keepalive:黑洞在调用开始 1.0 秒后出现;调用在 12.0 秒时返回(黑洞之后 11.0 秒):code=Unavailable message="keepalive ping failed to receive ACK within timeout"
  没有 keepalive:黑洞在调用开始 1.0 秒后出现;调用在 30.0 秒时返回(黑洞之后 29.0 秒):code=DeadlineExceeded message="context deadline exceeded"

服务端的 EnforcementPolicy 决定“客户端 PING 多频繁算太频繁”(MinTime)和“没有流时能不能 PING”(PermitWithoutStream)。客户端的 keepalive 参数要和它对上,否则连接会被关。

为什么需要

实测 · 本机执行输出
  正常运行 4.5 秒:服务端发出的 PING [1.0 2.0 3.0 4.0] 秒;客户端回的 ACK [1.0 2.0 3.0 4.0] 秒
  在途的流在黑洞开始后 1.5 秒收到 code=Unavailable message="error reading from server: EOF"

对端所在机器断电、网线被拔、中间的 NAT 或防火墙悄悄丢弃连接表项时,TCP 上没有任何通知。没有 keepalive 的调用只能一直等到自己的 deadline;有 keepalive 则在 Time + Timeout 之内发现。

PermitWithoutStream

实测 · 本机执行输出
  PermitWithoutStream=false:空闲 25 秒客户端 PING 0 个 [] 秒;有在途的流 25 秒 2 个 [9.9 19.9] 秒

默认 false:没有在途的流时不发 PING。要让空闲连接也被探测,要设为 true,并让服务端的策略允许。

分步演示

keepalive
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Go 1.27.1 / grpc-go v1.84.0 / protobuf-go v1.36.12 / golang.org/x/net v0.57.0 / protoc 36.2(Homebrew);服务端和客户端都是本机起的 Go 进程(127.0.0.1,端口 9800 以上,不访问任何外部服务);需要看线上字节时用我自己写的 TCP 抓包代理加 golang.org/x/net/http2 的 Framer 解析 HTTP/2 帧;计时用 time.Now 或 perf_counter,数字每次运行会有小幅抖动。PING 与 GOAWAY 的时刻是代理在线上解析 HTTP/2 帧得到的(相对每个场景的起点);“黑洞”是代理丢弃之后的所有数据,但保持 TCP 连接;六个场景并发运行;“客户端 Time 下限 10 秒、服务端 1 秒、maxPingStrikes = 2”读自 grpc-go 源码,其余是测量
    • 没有验证:其它语言的 gRPC 实现(Java、C++ 的 keepalive 默认值与下限是否相同);真实网络里 NAT、负载均衡器的空闲超时;TCP keepalive 与 gRPC keepalive 的相互作用;第三节里 PING 具体发出的时刻(按 Time 10 秒加 Timeout 2 秒推算,与 12.0 秒吻合,但这个场景没有单独抓 PING);服务端 MaxConnectionIdle、MaxConnectionAge 等(第 40 篇)
    京ICP备2026067502号-1京公网安备11010502063487号