gRPC 中的客户端重试

gRPC 提供了非常复杂的自动重试策略。其中按照重试时机可以分为简单 retry 和 hedging 两种。按照业务上划分可以分为透明重试和非透明重试。

text

简单 retry

其中简单 retry 配置如下:

1
2
3
4
5
6
7
8
9
"retryPolicy": {
"maxAttempts": 4,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": [
"UNAVAILABLE"
]
}

当 gRPC 收到不正常的 response 时,首先会通过以下几点来判断是否进行简单重试:

  1. 会通过 response 中对应的 status 来决定是否进行重试
  2. 请求本身是否超过 deadline
  3. 请求次数是否超过 maxAttemps,原始请求也被计算在 maxAttemps 中

重试间隔由 initialBackoff 和 maxBackoff 以及 backoffMultipiler 决定:

  1. 首次重试间隔为:random(0,initalBackoff)
  2. 第 n 次重试间隔为:min(initialBackoff * backoffMultiplier ^ (n-1), maxBackoff)

hedging policy 配置如下:

1
2
3
4
5
6
7
8
9
"hedgingPolicy": {
"maxAttempts": 4,
"hedgingDelay": "0.5s",
"nonFatalStatusCodes": [
"UNAVAILABLE",
"INTERNAL",
"ABORTED"
]
}

hedging

hedging 策略下当原始请求被发出后,当过了 hedingDelay 时间之后如果客户端还没有收到 response,就会发出 hedging request 请求,直到达到 maxAttemps 或者超过 deadline。

  1. 一旦客户端收到成功的 response ,则会立即取消掉其他的 request 。
  2. 如果收到 non-fatal 状态码,下一个 hedging 请求会立即发出,以缩短延时。

除了简单 retry 和 hedging 之外,重试也会受到下列配置的影响来避免服务端过载:

1
2
3
4
"retryThrottling": {
"maxTokens": 10,
"tokenRatio": 0.1
}

gRPC 内部会维护一个 token_count 来,token_count 初始值为 maxTokens ,并在 0 和 maxTokens 之间变动:

  1. 只有当 token_count 大于 threshold(maxTokens/2) 时才会进行重试,否则不予进行重试
  2. 每个失败的 RPC 会将 maxTokens -1
  3. 每个成功的 RPC 会将 token_count 增加 tokenRatio

透明重传和非透明重传

gRPC 的重试按照业务上划分可以分为三种:

  1. 请求从来没有离开过客户端
  2. 请求到达了服务端,但是并没有触达服务端业务层
  3. 请求触达了服务端业务层,但是失败了

前两种被称为透明重试,这种请求对于服务端是幂等的,同时也不会造成服务端过载,因此并不会受到上述 autoRetry 策略的影响,透明重试会用户完全透明,只有非透明重试才会收到上述策略的影响。

references