重试异步函数

本文档介绍如何请求异步(非 HTTPS)后台函数在失败时重试。

重试的语义

默认情况下,如果不启用重试,运行后台函数时所遵循的语义是“尽力而为”。也就是说,目标是运行函数一次,但不保证成功。

不过,如果为后台函数启用重试,语义会变为运行“至少一次”(注意,在数天内一直失败的函数最终可能会超时)。

为什么后台函数会无法完成

在极少数情况下,函数可能会由于内部错误而提早退出,并且默认情况下函数可能会自动重试,也可能不会自动重试。

更常见的情况是,后台函数可能由于函数代码本身抛出错误而无法成功完成。可能导致发生这种情况的一些原因如下:

  • 函数包含错误,且运行时抛出异常。
  • 函数无法访问服务端点,或者在尝试访问端点时超时。
  • 函数本身有意抛出异常(例如当某个参数验证失败时)。
  • 以 Node.js 编写的函数返回遭拒的 promise 或将非 null 值传递给回调函数。

在上述任何情况下,函数默认都会停止运行,而事件会遭舍弃。如果要在出错时重试函数,可通过设置“失败时重试”属性来更改默认的重试政策。这会导致事件在多达数天的时间里不断重试,直到函数成功完成。

如何启用重试

使用 API 控制台

您可以在 API 控制台中启用重试,步骤如下:

  1. 在 Cloud Platform Console 中转到 Cloud Functions 概览页面

  2. 点击创建函数。或者,您也可以通过修改现有函数来对该函数启用重试。

  3. 指定函数名称。

  4. 触发器字段中,选择后台函数触发器类型,例如 Pub/Sub 或 Cloud Storage。

  5. 填写函数的其他必填字段。

  6. 点击配置区域、超时和重试政策链接以展开高级设置。

  7. 选中标有失败时重试的复选框。

最佳做法

本部分介绍有关如何利用重试的最佳做法。

利用重试来应对暂时性错误

由于函数会一直重试,直到成功执行为止,因此在启用重试之前应进行彻底的测试,以从代码中清除 Bug 之类的永久性错误。重试最适合用于应对较有可能通过重试解决的间歇性/暂时性故障,比如不稳定的服务端点或超时。

设置结束条件以避免无限重试循环

在启用重试时,您应防止函数陷入连续循环,这是一项最佳做法。为此,您可以在函数开始处理之前添加明确定义的结束条件。一种简单而有效的方法是舍弃时间戳早于特定时间的事件。在发生永久性故障或持续时间长于预期的故障时,这样做有助于保证函数执行次数不会太多。

例如,以下代码段会舍弃超过 10 秒前发生的所有事件:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

结合使用 catch 与 promise

如果启用重试,应考虑向 promise 添加 catch。Cloud Functions 不理解不应重试的严重错误,所以如果您允许出现严重错误,就应相应地修改代码。

以下是一段代码示例:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

使可重试的后台函数遵循幂等原则

可重试的后台函数必须是幂等函数。下面是一些有关如何使后台函数具有幂等性的一般指导原则:

  • 许多外部 API(如 Stripe)允许提供幂等键作为参数。如果您在使用此类 API,应将事件 ID 作为幂等键。
  • 幂等性与“至少一次”机制非常契合,因为它能确保重试的安全性。编写可靠代码的一项常见最佳做法是结合使用幂等性和重试。
  • 确保代码具有内在的幂等性。例如:
    • 确保可以在保持运行结果不变的情况下进行多次更改。
    • 在事务中,先查询数据库状态再更改状态。
    • 确保所有副作用本身也具有幂等性。
  • 在函数之外强制执行事务检查(不依赖代码)。例如,在某个位置留存状态信息,并记录已处理事件的 ID。
  • 处理重复的带外函数调用。例如,设置一个单独的清理程序,在发生重复函数调用后执行清理。

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面