本文档介绍了如何请求异步(非 HTTPS)后台函数以在失败时重试。
重试语义
Cloud Functions 保证对事件源发出的每个事件至少执行一次事件驱动函数。但是,默认情况下,如果函数调用因错误而终止,则不会再次调用该函数,并且会删除该事件。当您在事件驱动函数上启用重试时,Cloud Functions 将重试失败的函数调用,直到它成功完成或重试窗口到期(默认情况下,7 天后)。
如果未为某个函数启用重试(这是默认设置),该函数始终会报告它已成功执行,并且200 OK
响应代码可能会出现在其日志中。即使函数遇到错误,也会发生这种情况。为清楚说明您的函数何时遇到错误,请确保适当地报告错误。
为什么事件驱动函数无法完成
在极少数情况下,函数可能会由于内部错误而过早退出,并且默认情况下该函数可能会或可能不会自动重试。
更典型的是,事件驱动的函数可能由于函数代码本身抛出的错误而无法成功完成。可能发生这种情况的一些原因如下:
- 该函数包含错误,运行时抛出异常。
- 该函数无法到达服务端点,或在尝试到达端点时超时。
- 该函数有意抛出异常(例如,当参数验证失败时)。
- 当用 Node.js 编写的函数返回被拒绝的承诺或将非
null
值传递给回调时。
在上述任何一种情况下,函数默认停止执行,事件被丢弃。如果您想在发生错误时重试函数,可以通过设置“失败时重试”属性来更改默认重试策略。这会导致事件重复重试最多多天,直到函数成功完成。
启用和禁用重试
使用 GCP 控制台
您可以在 GCP Console 中启用或禁用重试,如下所示:
进入云平台控制台的云函数概览页面。
单击创建函数。或者,单击现有函数以转到其详细信息页面,然后单击编辑。
为您的函数填写必填字段。
确保触发器字段设置为基于事件的触发器类型,例如 Cloud Pub/Sub 或 Cloud Storage。
单击更多展开高级设置。
选中或取消选中标记为Retry on failure的框。
在函数代码中
借助 Cloud Functions for Firebase,您可以在函数代码中启用重试。为后台函数执行此操作,例如functions.foo.onBar(myHandler);
,使用runWith
并配置失败策略:
functions.runWith({failurePolicy: true}).foo.onBar(myHandler);
如图所示设置为true
配置一个函数以在失败时重试。
最佳实践
本节介绍使用重试的最佳实践。
使用重试来处理暂时性错误
因为你的函数会不断重试直到成功执行,所以在启用重试之前应该通过测试从你的代码中消除像错误这样的永久性错误。重试最好用于处理间歇性/暂时性故障,这些故障在重试时很可能得到解决,例如不稳定的服务端点或超时。
设置结束条件以避免无限重试循环
最佳做法是在使用重试时保护您的函数免于连续循环。您可以通过在函数开始处理之前包含一个明确定义的结束条件来做到这一点。请注意,此技术仅在您的函数成功启动并且能够评估结束条件时才有效。
一种简单而有效的方法是丢弃时间戳早于特定时间的事件。这有助于避免在故障持续存在或比预期持续时间更长时执行过多。
例如,此代码片段会丢弃所有早于 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
与 Promises 结合使用
如果您的函数启用了重试,则任何未处理的错误都将触发重试。确保您的代码捕获不应导致重试的任何错误。
这是您应该做什么的示例:
return doFooAsync().catch((err) => {
if (isFatal(err)) {
console.error(`Fatal error ${err}`);
}
return Promise.reject(err);
});
使可重试的事件驱动函数幂等
可以重试的事件驱动函数必须是幂等的。以下是使此类函数幂等的一些一般准则:
- 许多外部 API(例如 Stripe)允许您提供一个幂等键作为参数。如果您正在使用这样的 API,您应该使用事件 ID 作为幂等键。
- 幂等性适用于至少一次交付,因为它可以安全地重试。因此,编写可靠代码的一般最佳做法是将幂等性和重试结合起来。
- 确保您的代码在内部是幂等的。例如:
- 确保突变可以发生不止一次而不会改变结果。
- 在改变状态之前查询事务中的数据库状态。
- 确保所有副作用本身都是幂等的。
- 独立于代码,在函数外强加事务检查。例如,在某处持久化状态记录给定事件 ID 已被处理。
- 处理带外的重复函数调用。例如,有一个单独的清理进程,在重复的函数调用后进行清理。