实测 gRPC 的 TLS/mTLS:证书各种问题时客户端看到的 Unavailable 错误文字;mTLS 缺证书、错 CA 的差别;服务端从 AuthInfo 读客户端身份;证书轮换只对新连接生效;握手的耗时。
实测 gRPC 的 per-RPC credentials:声明需要加密时明文连接会被拒绝,声明不需要时令牌在线上是明文;每个请求调用一次、HPACK 让重复令牌只剩几个字节;令牌过期前不提前刷新会被拒 9.2%;并发刷新不加锁会取 50 次;错误码的映射;连接级与单次凭证是叠加。
实测 protobuf 的兼容性:加字段安全且经过旧服务端不丢;改名二进制兼容但 JSON 破坏;改编号悄悄丢值;int32 改 sint32 值错位;复用编号不报错但含义错位;枚举新值保留数值;标量改 repeated 单向兼容。
实测 proto3 默认值与存在性:默认值不上线、服务端分不清没设置和设为 0;optional 多 2 字节记录有无;包装类型的做法;部分更新用 Merge 清不了零值;子消息和 nil getter;protojson 默认不输出零值、int64 是字符串。
实测 grpc-gateway v2.31.0:路径与查询参数映射、gRPC 状态码到 HTTP 状态码的多对一映射、JSON 体积是 protobuf 的 1.3 到 3.3 倍、经 gateway 的耗时是直连 gRPC 的 2 到 5 倍、自定义头默认不转发、客户端断开会取消后端、服务端流变成 JSON 行。
用本机 Chrome 实测:浏览器 fetch 直接调原生 gRPC 能拿到响应体,但读不到 grpc-status(HTTP/2 trailers 不暴露),跨域还因预检失败;grpc-web 把 trailers 变成响应体最后一帧(标志位 128),服务端流可以边收边读。
实测 grpc-go 服务端每个在途调用:1 个 goroutine、约 11 KiB(堆约 3 KiB + 栈约 8 KiB);NumStreamWorkers 在 worker 忙时仍新开 goroutine,不能限流,信号量可以;不看 ctx 的 handler 在客户端取消后仍占着 goroutine。
实测 grpc-go 序列化与内存:Marshal/Unmarshal 分配约等于 payload;整个 echo RPC 大消息稳定分配 payload 的 3~4 倍;小消息每次约 11 KiB 固定开销;1 MiB 中文 string 的反序列化比 bytes 慢约 12 倍;缓冲池的开关在这个版本不导出。
实测 gRPC 调用链上的超时叠加:各层取最小;内层比外层剩余时间还长的超时不起作用;deadline 以剩余时间的形式(grpc-timeout 头)每一跳递减;A 重试 B 3 次会吃掉外层的预算;转发调用不传 ctx 会让下游白做工作。
实测 grpc-go 的优雅停机:Stop 立即让在途调用失败,GracefulStop 等到做完;一个不结束的流能让 GracefulStop 永远等下去;负载下滚动重启 Stop 失败 8 个而 GracefulStop 0 个;Stop 默认不等 handler 返回,WaitForHandlers 才等。
实测 gRPC 流泄漏:没读完也没取消的流在服务端一直存在,GC 回收不了;泄漏的流占满 MaxConcurrentStreams 会拖死同一连接上的其它调用;CloseSend 不释放服务端流,cancel 或读到 EOF 才算;客户端流必须 CloseAndRecv;泄漏的流让连接永远不空闲。
本机实测 gRPC 的吞吐曲线:小消息从 1 条连接增加到 4 条吞吐增加约三成,之后到顶;64 KiB 消息 1 条连接就有约 1369 MiB/秒;并发调用者从 1 个到 256 个吞吐涨十倍、p50 延迟从 79 µs 涨到 2090 µs;无并发时流式比 Unary 快约 30%。