gRPC 在生产环境用了两年之后
内部服务全面从 HTTP/JSON 切到 gRPC 两年了,当初写迁移方案的人里还在组的只剩我一个。趁着记忆还热,把两年里沉淀下来的经验写一篇,算是给这套选型做个中期复盘。 选对了什么: 强类型契约的价值被严重低估了。 proto 文件 + 代码生成让接口变更的 review 变得极其直观,破坏性变更在 CI 阶段就能拦住。两年里因为接口不一致引发的线上问题,是零。以前 HTTP/JSON 时代一年总有那么两三次。 grpc-go 的拦截器生态很好用。 我们把日志、trace、限流、panic recovery 全部收敛在 server 端拦截器里,业务代码完全不用感知。这是迁移以来工程体验提升最大的部分。 踩过的坑,按疼痛程度排序: 默认负载均衡是"假"的。 gRPC 客户端和一条 HTTP/2 长连接通信,如果用普通的 DNS 解析 + 单连接,所有请求会打到同一个后端实例上。必须用 dns:// resolver 加上客户端的 round-robin,或者上 xDS。这个问题在我们灰度的时候表现为"新版本流量永远打不到新实例",排查了整整两天。 keepalive 参数两端要协商好。 中间一层的 LVS 把"空闲"的 gRPC 连接静默掐掉,客户端还以为连接健在,请求 timeout 谷底式抖动。ping 间隔改到小于 LVS 的空闲超时之后解决。这种网络设备层的问题文档里不会写,只能靠抓包。 deadline 必须全链路传递。 没传 context 的下游超时设置,等于没有超时。我们后来在 lint 里加了规则,gRPC 调用不传 ctx 直接报错。 一个想法:gRPC 的成熟度曲线是陡峭的——入门半天,但进入生产要补的课(连接管理、负载均衡、可观测性)是一整个学期。如果重来一次,小团队内部系统我还是会先推荐 HTTP/JSON,等服务和调用量到了一定规模再切 gRPC,收益才盖得住成本。