这个坑值得记一笔。Go 服务用 grpc-go 1.65 调推理后端的流式接口,QPS 30 时很稳,压到 200 就有大量请求既不返回也不报错,连接复用率显示为 1。抓包看到服务端宣告的 MAX_CONCURRENT_STREAMS 是 100,超出的请求堆在客户端,把连接数调大仍然卡。
刚开始盯着「服务端流控窗口偏小,流建立后也会长时间停顿」,折腾了半个多小时,排掉了。
这类报错背后常见的原因有几处,逐个排除就行。单连接默认只允许 100 个并发流,超出的在客户端排队;连接池没按并发度扩容,所有请求挤在一条连接上;客户端没设超时,排队的请求一直等着不释放。
第一件事是看服务端宣告的并发流上限
python -c "print('MAX_CONCURRENT_STREAMS=100')"
然后把连接池按并发度扩容
python -c "print('pool size 4, stream limit 100')"
给每次调用加 3 秒超时
python -c "print('context.WithTimeout(ctx, 3*time.Second)')"
压测确认排队长度归零
python -c "print('200 qps, p99 under 800ms')"
别只看报错消失,验证要多做一步。200 QPS 下 p99 在 800ms 以内,客户端排队长度为 0,服务端并发流数稳定在 240 左右。
gRPC 的并发能力等于连接数乘以单连接流上限,复用一条连接不等于无限并发,压测前先把这两个数乘一遍。这类问题最好一次改到位,反复小改反而容易把原因搅混。
