重试策略

本页面介绍重试策略,例如向 Cloud Storage 发出的失败请求的截断指数退避算法。

概览

如需确定是否重试向 Cloud Storage 发出的失败请求,请考虑请求的类型及其幂等性,幂等性决定了重试操作是否安全。通常,您应使用截断指数退避算法重试以下请求类型:

  • 所有发送到 Cloud Storage 且返回 HTTP 5xx 响应代码的请求(包括数据或元数据的上传和下载请求)。

  • 发送到 Cloud Storage 且返回 HTTP 429 响应代码的请求(不包括使用可续传上传来上传数据的请求)

  • 可返回 HTTP 408 响应代码的可续传上传XML API 分段上传

  • 套接字超时和 TCP 断开连接。

如需了解详情,请参阅 JSONXML 的状态和错误代码。

指数退避算法

截断指数退避算法是适用于网络应用的标准错误处理策略,使用这种策略时,客户端会定期重试失败的请求,并不断增加各次请求之间的延迟时间。

指数退避算法以指数方式重试请求(不断增加各次重试之间的等待时间,直到达到最大退避时间)。示例如下:

  1. 向 Cloud Storage 发出请求。

  2. 如果请求失败,请等待 1 + random_number_milliseconds 秒后再重试请求。

  3. 如果请求失败,请等待 2 + random_number_milliseconds 秒后再重试请求。

  4. 如果请求失败,请等待 4 + random_number_milliseconds 秒后再重试请求。

  5. 依此类推,等待时间上限为 maximum_backoff

  6. 继续等待并重试,直到达到时间上限 (deadline),但不会增加各次重试之间的 maximum_backoff 等待时间。

其中:

  • 等待时间为 min((2n +random_number_milliseconds),maximum_backoff),其中,n 会在每次迭代(请求)后增加 1。

  • random_number_milliseconds 是小于或等于 1000 的毫秒数(随机值)。这有助于避免出现以下情况:许多客户端同步进行处理并同时执行重试操作,导致同步发送每一波请求。每次重试请求后,系统都会重新计算 random_number_milliseconds 值。

  • maximum_backoff 通常为 32 或 64 秒。哪个值更为适当,这取决于用例。

达到 maximum_backoff 时间后您可以继续重试,但我们建议您的请求在一定时间后失败,以防止应用无响应。例如,如果客户端使用的 maximum_backoff 时间为 64 秒,则在达到此值后,客户端可以每 64 秒重试一次。然后,客户端在 600 秒的 deadline 后停止重试。

客户端在各次重试之间应等待的时长以及应重试的次数取决于您的用例和网络条件。例如,与桌面客户端相比,同一应用的移动客户端可能需要重试更多的次数和设置更长的时间间隔。

如果在超过 maximum_backoff 以及任何允许的额外重试时间之后,请求仍然失败,请使用支持和帮助下列出的其中一种方法报告或记录错误。

幂等性

如需确定重试向 Cloud Storage 发出的失败请求是否安全,请考虑该请求是否具有幂等性,这意味着多次应用同一操作对目标资源的状态产生相同的影响。重试幂等操作通常是安全的。

以下是满足幂等性的条件的示例:

  • 即使持续请求目标资源,操作也会对目标资源产生相同的可观察到的影响。

  • 操作仅成功一次。

  • 操作不会对目标资源的状态产生任何可观察到的影响。

例如,即使请求多次成功,列出存储分区的请求也会产生相同的影响。另一方面,创建新的 Pub/Sub 通知等操作不具有幂等性,因为每次请求成功时该操作都会创建新的通知 ID。

条件式幂等性

一部分请求具有条件式幂等性,这意味着这些请求仅在包含特定可选参数时才具有幂等性。默认情况下,只有在条件 case 通过时,才应对重试有条件安全的操作进行重试。Cloud Storage 接受前提条件和 ETag 作为请求的条件 case。

每个 Cloud Storage 工具的重试策略

点击以下标签页,可查看每个 Cloud Storage 工具的重试策略建议。

控制台

Cloud Console 会代表您向 Cloud Storage 发送请求,并处理任何必要的退避操作。

gsutil

gsutil 会重试概览部分中列出的错误,而无需您执行其他操作。您可能需要对其他错误执行操作,例如:

  • 凭据无效或权限不足。

  • 由于代理配置问题,网络不可达。

  • 在使用 -m 顶级标志的命令中失败的个别操作。

对于可重试的错误,gsutil 会使用截断的二进制指数退避策略重试请求。默认情况下,gsutil 会在 1+2+4+8+16+32+60... 秒内重试 23 次,持续约 10 分钟:

  • 如果请求失败,则等待 [0..1] 秒之间的随机一段时间后再重试;
  • 如果请求再次失败,请等待 [0..2] 秒之间的随机时间段,然后重试;
  • 如果请求再次失败,请等待 [0..4] 秒之间的随机时间段,然后重试;
  • 依此类推,最多重试 23 次,且重试次数最长为 60 秒。

您可以通过编辑 .boto 配置文件 "[Boto]" 部分中的 num_retriesmax_retry_delay 配置变量来配置重试次数和任何单个重试的最大延迟。

对于使用 gsutil cprsync 命令进行的数据传输,gsutil 以可续传传输的形式提供了额外的重试功能。

客户端库

C++

C++ 客户端库默认使用指数退避算法

C#

C# 客户端库默认使用指数退避算法

Go

Go 客户端库默认使用指数退避算法

Java

Java 客户端库默认使用指数退避算法

Node.js

Node.js 可以自动使用退避策略来重试带有 autoRetry 参数的请求。

PHP

PHP 客户端库默认使用指数退避算法

Python

对于重试策略,Python 客户端库会区分媒介和非媒介操作:

  • 媒介操作包括将载荷数据提取或发送到对象的所有操作。例如,包括以字词“upload”或“download”开头的 Blob 以及 Client.download_blob_to_file 的所有方法。

  • 非媒介操作是指仅处理对象元数据的操作。

默认情况下,媒介和非媒介操作支持重试以下错误代码:

  • 连接错误:
    • requests.exceptions.ConnectionError
    • requests.exceptions.ChunkedEncodingError(仅限媒介 API 调用)
  • HTTP 代码:
    • 429 Too Many Requests
    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
    • 508 Resource Limit Exceeded

通过 Python 执行的操作使用以下指数退避算法默认设置:

默认设置 媒介调用 非媒介调用
初始等待时间(秒) 1 1
每次迭代的等待时间倍数(秒) 2 2
最长等待时间(秒) 64 60
默认时限(秒) 600 120
已实现抖动

媒体和非媒体操作的子集仅在具有特定可选参数时遵循幂等原则。默认情况下,只有在条件满足的情况下,才会重试符合条件安全的重试操作。目前,这些条件包括:

  • DEFAULT_RETRY_IF_GENERATION_SPECIFIED

    • 如果已将 if generationif_generation_match 作为参数传入方法,则重试安全。通常,方法只会接受这两个参数中的一个。
  • DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED

    • 如果已将 if_metageneration_match 作为参数传入方法,则重试安全。
  • DEFAULT_RETRY_IF_ETAG_IN_JSON

    • 如果方法将 etag 插入到 JSON 请求正文,则重试安全。对于 HMACKeyMetadata.update(),这意味着必须对 HMACKeyMetadata 对象本身设置 etag。对于其他类的 set_iam_policy() 方法,这意味着必须在传入方法的“policy”参数中设置 etag。

针对媒介操作的重试政策

对于媒体操作,可以为上载方法配置 num_retries 参数以指定上载重试的次数。默认情况下,仅重试具有 if_metageneration_match 条件的上载以保证幂等。设置 num_retries 参数会覆盖默认行为,即使没有 if_metageneration_match 条件,也可以保证重试。

针对非媒介操作的重试政策

重试安全或有条件安全的非媒介操作为其方法签名添加了 retry 参数。这些参数的默认值是以下值之一:

  • DEFAULT_RETRY
  • DEFAULT_RETRY_IF_GENERATION_SPECIFIED
  • DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
  • DEFAULT_RETRY_IF_ETAG_IN_JSON

如需修改默认重试行为,请使用 with_XXX 方法调用 google.cloud.storage.retry.DEFAULT_RETRY 对象来创建该对象的副本。例如,如需将默认时限修改为 30 秒,请传递 retry=DEFAULT_RETRY.with_deadline(30)。我们建议您逐个修改特性。如需了解详情,请参阅 google-api-core 重试 参考文档。

如需配置您自己的条件性重试,请创建 ConditionalRetryPolicy 对象,并使用 DEFAULT_RETRY_IF_GENERATION_SPECIFIEDDEFAULT_RETRY_IF_METAGENERATION_SPECIFIEDDEFAULT_RETRY_IF_ETAG_IN_JSON 封装自定义 Retry 对象。

以下是自定义条件性重试的示例:

  • blob.reload() 默认使用 DEFAULT_RETRY。如需将其替换以便完全不会重试该函数,请调用为 blob.reload(retry=None)

  • bucket.update() 默认使用 DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED。要替换此设置,使得即使未指定元数据世代编号,函数也会重试,并按如下方式调用该函数:

    from google.cloud.storage.retry import DEFAULT_RETRY
    bucket.update(retry=DEFAULT_RETRY)
  • bucket.list_blobs() 默认使用 DEFAULT_RETRY。要替换此设置,以使 API 调用以 20 秒(而非默认的 120 秒)为截止时间,请按如下方式调用该函数:

    from google.cloud.storage.retry import DEFAULT_RETRY
    modified_retry = DEFAULT_RETRY.with_deadline(20)
    bucket.list_blobs(retry=modified_retry)

Ruby

Ruby 客户端库默认使用指数退避算法

REST API

直接调用 JSON 或 XML API 时,您应该使用指数退避算法来实现您自己的重试策略。

操作的幂等性

下表列出了属于每种幂等性的 Cloud Storage 操作。

幂等性 运维
始终具有幂等性
  • 所有获取和列出请求
  • 插入或删除存储分区
  • 测试存储分区 IAM 政策和权限
  • 锁定保留政策
  • 删除 HMAC 密钥或 Pub/Sub 通知
有条件地具有幂等性
  • 针对将 IfMetagenerationMatch 或 ETag 作为 HTTP 前提条件的存储分区的更新/修补请求
  • 针对将 IfMetagenerationMatch 或 ETag 作为 HTTP 前提条件的对象的更新/修补请求
  • 将 ETag 作为 HTTP 前提条件或在资源正文中设置存储分区 IAM 政策
  • 将 ETag 作为 HTTP 前提条件或在资源正文中更新 HMAC 密钥
  • 使用 ifGenerationMatch 插入、复制、编写或重写对象
  • 使用 ifGenerationMatch 删除对象(对于对象版本,则使用世代编号)
永不幂等
  • 创建 HMAC 密钥
  • 创建 Pub/Sub 通知
  • 创建、删除或发送针对存储分区和对象 ACL 或默认对象 ACL的修补/更新请求

后续步骤