keepalive 是 gRPC 在 HTTP/2 PING 帧上做的连接探活。它怎么设、有哪些隐藏的下限、客户端和服务端的配置不匹配会怎样、对端悄悄消失时能多快发现?这篇用本机服务端和一个能丢弃数据的代理逐项实测。
客户端 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 就认为连接已死并关掉。客户端和服务端各自都可以发。
客户端 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=false:空闲 25 秒客户端 PING 0 个 [] 秒;有在途的流 25 秒 2 个 [9.9 19.9] 秒
默认 false:没有在途的流时不发 PING。要让空闲连接也被探测,要设为 true,并让服务端的策略允许。