知其然,知其所以然
gRPC 2026-10-12

一个 unary 调用在网线上长什么样:HTTP/2 帧、头和 5 字节消息前缀

用自己写的 TCP 抓包代理看一次 gRPC Unary 调用的 HTTP/2 帧:请求是 HEADERS 加 DATA,响应是 HEADERS、DATA 和带 grpc-status 的 trailers;消息是 5 字节前缀加 protobuf;首次调用请求头 82 字节,第二次因为 HPACK 只有 15 字节。

gRPC HTTP/2 帧 protobuf 抓包 HPACK Go 真实验证 动画演示
gRPC 2026-10-12

gRPC 的消息帧:压缩标志、长度与 protobuf 载荷

抓包看 gRPC 的消息帧:消息边界由 5 字节前缀决定,和 HTTP/2 DATA 帧边界无关(20000 字节切成 16384 + 3628,1 MiB 切成 65 帧);gzip 压缩是逐条消息的,标志位为 1、长度是压缩后的;超过 4 MiB 的消息服务端读到长度就拒绝,线上只传了一小部分。

gRPC 消息帧 5 字节前缀 DATA 压缩 最大消息 Go 真实验证 动画演示
gRPC 2026-10-12

gRPC 状态码:十几个 code 各自对应什么场景

实测 gRPC 的 17 个状态码:线上传的是数字;普通 error 一律变成 Unknown;连接失败默认立刻返回 Unavailable(0.4 ms),加 WaitForReady 才会等;网关返回 429/502/503/504 在客户端都是 Unavailable,500 却是 Unknown。

gRPC 状态码 错误处理 Unavailable WaitForReady HTTP 状态映射 Go 真实验证 动画演示
gRPC 2026-10-12

grpc-status 与 grpc-message 为什么放在 trailers 里

为什么 grpc-status 放在 trailers:HTTP 状态 200 在第一个 HEADERS 就发出去了,流中途出错只能放在最后;一条消息都没发就失败是 trailers-only 的单个 HEADERS;grpc-message 做百分号编码;-bin 键用 base64;连接中途断了得到 Unavailable;缺少 trailers 得到 Internal。

gRPC trailers grpc-status grpc-message HTTP/2 百分号编码 Go 真实验证 动画演示
gRPC 2026-10-12

deadline:截止时间怎么跨服务传播

实测 gRPC 的 deadline:线上是最多 8 位数字加单位字母的相对时长(999978u);A→B→C 逐跳扣减(999、898、797 ms);中间一层不传 ctx,客户端超时后下游还会把活干完;已过期的调用不发任何帧;服务端不监听 ctx 就看不到取消;传输延迟不会被扣掉。

gRPC deadline grpc-timeout context 超时传播 Go 真实验证 动画演示
gRPC 2026-10-12

context 取消:客户端取消后服务端 goroutine 怎么停

实测 gRPC 取消:cancel 是一个 RST_STREAM(CANCEL),deadline 到期两边各发一个;handler 不监听 ctx 就会继续跑完;流式 Send 在取消后失败并带 Canceled;取消与响应赛跑可能成功也可能 Canceled;调用完成后 cancel() 不发帧;取消沿调用链几乎同时传到下游。

gRPC 取消 context RST_STREAM goroutine 流式 竞态 Go 真实验证 动画演示
gRPC 2026-10-12

metadata:头和 trailer 里能放什么,大小写与二进制值

实测 gRPC metadata:键统一小写;普通键只能放可打印 ASCII,-bin 键可放任意字节(线上无填充 base64);user-agent、content-type、grpc-timeout 等库占用的头被静默丢弃;同键多值是多行;头总大小 16 MiB 附近断开;响应头必须在第一条消息之前设置。

gRPC metadata 请求头 -bin 响应头 trailers Go 真实验证 动画演示
gRPC 2026-10-12

服务端流:发送缓冲、流控与慢消费者

服务端流的流控实测:快生产者遇到慢消费者,Send 从 0.4 ms 变成 31 ms 跟着消费者走;不读数据时默认只能先发 195584 字节;调大接收窗口可以多发,窗口选项会关闭动态窗口;被阻塞的 Send 在取消后返回 Canceled;handler 返回后已发的消息仍会送达。

gRPC 服务端流 流控 慢消费者 窗口 背压 Go 真实验证 动画演示
gRPC 2026-10-12

客户端流与双向流:什么时候 Recv 会返回 EOF

客户端流和双向流的结束语义:CloseSend 在线上是长度 0 的 DATA 帧加 END_STREAM;半关闭后服务端还能继续发;服务端提前结束时客户端 Send 一律返回 io.EOF,状态要靠 Recv 取;只 Send 不 Recv 会在 5 条(1280 KiB)后死锁;忘了 CloseSend 服务端一直等。

gRPC 客户端流 双向流 CloseSend io.EOF 死锁 Go 真实验证 动画演示
gRPC 2026-10-12

HTTP/2 流控窗口:初始窗口与 BDP 估计怎么影响吞吐

实测 HTTP/2 流控窗口:吞吐大致等于窗口除以往返时间——RTT 20 ms 时 64 KiB 窗口只有 2.5 MiB/s、4 MiB 窗口 119.5;默认的动态窗口靠 PING 测量 BDP 逐步长大;4 个流共享连接时连接窗口 64 KiB 把总吞吐压到 9.1 MiB/s(4 MiB 时 402.7);小消息不受影响。

gRPC HTTP/2 流控 窗口 BDP 吞吐 延迟 Go 真实验证 动画演示
gRPC 2026-10-12

连接管理:一个 ClientConn 里的多个子连接与重连退避

实测 gRPC 客户端连接管理:NewClient 惰性建连;pick_first 只连一个地址、round_robin 连全部;默认退避 1 秒起乘 1.6;断开后进入 IDLE 等下一次调用;GracefulStop 发两个 GOAWAY;WithIdleTimeout 让空闲连接回到 IDLE。

gRPC 连接管理 ClientConn 重连 退避 GOAWAY 空闲超时 Go 真实验证 动画演示
gRPC 2026-10-12

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

实测 gRPC keepalive:客户端 Time 有 10 秒下限;服务端默认策略下客户端 PING 到第三个就被 GOAWAY(too_many_pings);对端变成黑洞时有 keepalive 12 秒发现、没有的要等到 deadline;服务端也能探活;PermitWithoutStream 默认 false 时空闲连接不探测。

gRPC keepalive PING GOAWAY too_many_pings EnforcementPolicy 连接探活 Go 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号