gRPC 的重试是怎么工作的?配置里写 maxAttempts 就一定会试那么多次吗?什么情况下不会重试?hedging 呢?这篇用一个会按次数失败的服务端逐项验证。
没有配置:服务端收到 1 次;结果 code=Unavailable message="第 1 次失败" 有配置:服务端收到 3 次;结果 成功;总耗时 171.8 ms;每次到达的间隔 [57 112] ms 线上每个请求的 grpc-previous-rpc-attempts 头:[(无) 1 2] 退避的取值范围(源码:initialBackoff × multiplier^n,再乘以 0.8~1.2 的随机数):第 1 次重试前 40–60 ms,第 2 次重试前 80–120 ms 再重复 8 次的间隔:[51 116] ms [48 87] ms [47 95] ms [46 118] ms [53 96] ms [58 87] ms [45 109] ms [45 121] ms
写在服务配置的 methodConfig 里:maxAttempts、initialBackoff、maxBackoff、backoffMultiplier、retryableStatusCodes。每次重试前等待 initialBackoff × multiplier^n,再乘以 0.8~1.2 的随机数。重试的请求会带 grpc-previous-rpc-attempts 头。
服务端返回 Internal,只允许 UNAVAILABLE 重试:服务端收到 1 次;code=Internal message="第 1 次失败" 一直 Unavailable,配置 maxAttempts = 4:服务端收到 4 次;code=Unavailable message="max retries exhausted: failed after 4 attempts: rpc error: code = Unavailable de…" 一直 Unavailable,配置 maxAttempts = 10:服务端收到 5 次;code=Unavailable message="max retries exhausted: failed after 5 attempts: rpc error: code = Unavailable de…"
错误码必须在 retryableStatusCodes 里;客户端把 maxAttempts 限制到 5(WithMaxCallAttempts 可调)。
handler 里 SetHeader=false:服务端收到 3 次;成功 handler 里 SetHeader=true :服务端收到 1 次;code=Unavailable message="第 1 次失败"
响应头已经发出(不是 Trailers-Only)就不重试;请求已经超过重试缓冲(默认 256 KiB)也不重试。所以失败的响应上不要先写响应头。
服务端收到 2 次,每次到达的间隔 [105] ms;调用 252.2 ms 后返回:code=DeadlineExceeded message="context deadline exceeded"
重试的总时间受调用的 deadline 约束,退避等待期间 deadline 到期就直接以 DeadlineExceeded 结束。
pushback = 300:服务端收到 2 次;到达间隔 [303] ms;总耗时 305.5 ms;成功 pushback = -1 :服务端收到 1 次;到达间隔 [] ms;总耗时 1.3 ms;code=Unavailable message="第 1 次失败"
trailer grpc-retry-pushback-ms 为非负数时,客户端按它等待;为负数时不再重试。
请求 100 KiB:服务端收到 3 次;成功 请求 300 KiB:服务端收到 1 次;code=Unavailable message="第 1 次失败"
重试要重发请求,所以客户端要把请求缓存起来;缓冲有上限。
hedgingPolicy:服务端收到 1 次;总耗时 605.1 ms;成功
grpc-go 当前没有实现 hedging(源码里只有 TODO),配置里写了会被忽略。