请求对冲

解释了什么是请求对冲以及如何配置它。

请求对冲

解释了什么是请求对冲以及如何配置它。

概述

对冲是 gRPC 支持的两种可配置重试策略之一。通过对冲,gRPC 客户端将同一请求的多个副本发送到不同的后端,并使用收到的第一个响应。随后,客户端取消所有未完成的请求并将响应转发给应用程序。

Basic hedging diagram

使用场景

对冲是一种在大规模分布式系统中减少尾部延迟的技术。虽然简单的实现可能会给后端服务器带来显著的负载,但可以在适度增加负载的同时获得大部分的延迟减少效果。

有关尾部延迟的深入讨论,请参阅 Jeff Dean 和 Luiz André Barroso 的经典文章 大规模应用中的尾部延迟

在 gRPC 中配置请求对冲

对冲可以通过 gRPC 服务配置 进行配置,粒度为每个方法。配置包含以下参数:

"hedgingPolicy": {
  "maxAttempts": INTEGER,
  "hedgingDelay": JSON proto3 Duration type,
  "nonFatalStatusCodes": JSON array of grpc status codes (int or string)
}
  • maxAttempts:在等待成功响应时,最大并发请求数。这是一个必填字段,必须指定。如果指定值大于 5,gRPC 将使用值 5
  • hedgingDelay:在等待成功响应时,客户端发送下一个请求之前需要经过的时间量。此字段是可选的,如果未指定,则所有 maxAttempts 个请求将同时发出。
  • nonFatalStatusCodes:一个可选的 gRPC 状态码列表。如果任何对冲请求失败且状态码不在此列表中,则所有未完成的请求都将被取消,并将响应返回给应用程序。

对冲策略

当应用程序发起包含服务配置中 hedgingPolicy 配置的 RPC 调用时,原始 RPC 会立即发送,与标准的非对冲调用一样。如果在 hedgingDelay 时间后仍未收到成功响应,则会发出第二个 RPC。如果再次经过 hedgingDelay 时间后两个 RPC 均未收到响应,则会发送第三个 RPC,依此类推,最多发送 maxAttempts 个请求。gRPC 调用截止时间应用于整个对冲请求链。一旦超过截止时间,无论是否有正在进行的 RPC,也无论对冲配置如何,操作都会失败。

收到成功响应后(响应于任何对冲请求),所有未完成的对冲请求都将被取消,并将响应返回给客户端应用程序层。

如果从对冲请求收到带有非致命状态码(由 nonFatalStatusCodes 字段控制)的错误响应,则立即发送队列中的下一个对冲请求,跳过其对冲延迟。如果收到任何其他状态码,所有未完成的 RPC 都将被取消,并将错误返回给客户端应用程序层。

如果对冲 RPC 的所有实例都失败,则不会进行额外的重试尝试。本质上,对冲可以被视为在甚至收到失败之前就对原始 RPC 进行重试。

如果在对冲请求的响应中收到指定不重试的服务器回退,则不应为该调用发出更多对冲请求。

限制对冲 RPC

gRPC 提供了一种限制对冲 RPC 的方法,以防止服务器过载。也可以通过服务配置使用 RetryThrottlingPolicy 消息配置限制。限制配置包含以下内容:

"retryThrottling": {
  "maxTokens": 10,
  "tokenRatio": 0.1
}

对于每个服务器名称,gRPC 客户端维护一个 token_count,该计数器最初设置为 max_tokens。每个出站 RPC(无论调用哪个服务或方法)都会按如下方式更改 token_count

  • 每个失败的 RPC 会将 token_count1
  • 每个成功的 RPC 会将 token_count 增加 token_ratio

使用对冲时,第一个请求总是发送出去,但只有当 token_count 大于阈值(定义为 max_tokens / 2)时,才会发送后续的对冲请求。如果 token_count 小于或等于阈值,则对冲请求不会阻塞。相反,它们会被取消,如果没有其他已发送的对冲 RPC,则会将失败返回给客户端应用程序。

只有失败状态码符合非致命状态码条件或收到指示不重试的回退响应的请求才会计入限制策略的失败次数。这避免了将服务器故障与对格式错误请求的响应(例如 INVALID_ARGUMENT 状态码)混淆。

服务器回退

服务器可以通过在其响应中设置元数据来明确回退客户端。如果回退指示不重试,则不会发送更多对冲请求。如果回退指示在给定延迟后重试,则(如果存在)下一个对冲请求将在给定延迟时间后发出。

服务器回退使用元数据键 grpc-retry-pushback-ms 指定。该值是一个 ASCII 编码的带符号 32 位整数,没有不必要的前导零,表示在发送下一个对冲请求之前需要等待多少毫秒。如果回退值为负数或无法解析,则会被视为服务器要求客户端完全不重试。

资源

语言支持

语言示例
JavaJava 示例
C++暂未提供
Go暂不支持
上次修改时间 2023 年 10 月 3 日: 添加请求对冲指南 (#1199) (d357308)