你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
Azure Functions 错误处理和重试
处理 Azure Functions 中的错误对于帮助避免丢失数据、避免遗漏事件以及监视应用程序的运行状况非常重要。 这也是帮助你了解基于事件的触发器的重试行为的重要方式。
本文介绍了错误处理的常规策略和可用的重试策略。
重要
2022 年 12 月删除了某些触发器的预览重试策略支持。 支持的触发器的重试策略现已正式发布(正式版)。 有关当前支持重试策略的扩展列表,请参阅重试部分。
处理错误
Azure 函数中发生的错误可能来自:
- 使用内置函数触发器和绑定。
- 调用底层 Azure 服务的 API。
- 调用 REST 终结点。
- 调用客户端库、包或第三方 API。
为避免丢失数据或遗漏消息,必须遵循良好的错误处理做法。 下表介绍了一些建议的错误处理做法,并提供指向详细信息的链接。
建议 | 详细信息 |
---|---|
启用 Application Insights | Azure Functions 与 Application Insights 集成,可用于收集错误数据、性能数据和运行时日志。 应使用 Application Insights 发现并更好地了解函数执行中发生的错误。 若要了解详细信息,请参阅监视 Azure Functions。 |
使用结构化错误处理 | 捕获和记录错误对于监视应用程序的运行状况至关重要。 任何函数代码的最顶层应包含 try/catch 块。 在 catch 块中,可以捕获并记录错误。 有关绑定可能引发哪些错误的信息,请参阅绑定错误代码。 根据具体的重试策略,可能还会引发新的异常以再次运行函数。 |
规划重试策略 | 多个 Functions 绑定扩展为重试提供内置支持,而其他函数则允许定义由 Functions 运行时实现的重试策略。 对于不提供重试行为的触发器,应考虑实现自己的重试方案。 有关详细信息,请参阅重试。 |
幂等性设计 | 处理数据时发生错误可能会给函数带来问题,尤其是在处理消息时。 必须考虑错误发生时会发生什么以及如何避免重复处理。 若要了解有关详细信息,请参阅针对完全相同的输入设计 Azure Functions。 |
重试
有两种可用于函数的重试:
- 单个触发器扩展的内置重试行为
- Functions 运行时提供的重试策略
下表指示哪些触发器支持重试以及配置重试行为的位置。 它还链接到有关来自底层服务的错误的详细信息。
触发器/绑定 | 重试源 | 配置 |
---|---|---|
Azure Cosmos DB | 重试策略 | 函数级 |
Blob 存储 | 绑定扩展 | host.json |
事件网格 | 绑定扩展 | 事件订阅 |
事件中心 | 重试策略 | 函数级 |
Kafka | 重试策略 | 函数级 |
队列存储 | 绑定扩展 | host.json |
RabbitMQ | 绑定扩展 | 死信队列 |
服务总线 | 绑定扩展 | host.json* |
计时器 | 重试策略 | 函数级 |
*需要 Azure 服务总线扩展版本 5.x。 在较旧的扩展版本中,由服务总线死信队列实现重试行为。
重试策略
Azure Functions 允许为运行时强制实施的特定触发器类型定义重试策略。 这些触发器类型当前支持重试策略:
对于 v1 和 v2 Python 编程模型,重试支持是相同的。
Functions 运行时版本 1.x 不支持重试策略。
重试策略指示运行时重新运行失败的执行,直到成功完成或达到最大重试次数。
当受支持触发器类型执行的函数引发未捕获的异常时,将评估重试策略。 最佳做法是,应捕获代码中的所有异常,并针对要导致重试的任何错误引发新异常。
重要
在执行重试策略完成后,才会写入事件中心检查点。 由于此行为,特定分区上的进度会暂停,直到当前批处理完成处理。
事件中心扩展的版本 5.x 支持用于 Functions 主机与事件中心之间的交互的其他重试功能。 有关详细信息,请参阅事件中心 host.json 参考中的 clientRetryOptions
。
重试策略
可以配置策略支持的两种重试策略:
在消耗计划中运行时,只需在函数代码执行时计费。 在上述任一重试策略中,执行之间的等待时间不会计费。
最大重试计数
可以在最终失败之前配置重试函数执行的最大次数。 当前重试计数存储在实例的内存中。
实例可能在每两次重试之间发生故障。 当实例在重试策略实施过程中发生故障时,重试计数会丢失。 出现实例失败时,事件中心触发器能够恢复处理,在新实例上重试该批次,并将重试计数重置为零。 计时器触发器不会在新实例上恢复。
此行为意味着系统只能尽力完成最大重试次数。 在某些罕见情况下,重试某个执行的次数可能会超过请求的最大次数。 对于计时器触发器,重试次数可能会小于请求的最大次数。
重试示例
为固定延迟和指数退避策略提供了示例。 若要查看特定策略的示例,必须先在上一选项卡中选择该策略。
以下 NuGet 包支持函数级重试:
- Microsoft.Azure.Functions.Worker.Sdk>= 1.9.0
- Microsoft.Azure.Functions.Worker.Extensions.EventHubs>= 5.2.0
- Microsoft.Azure.Functions.Worker.Extensions.Kafka>= 3.8.0
- Microsoft.Azure.Functions.Worker.Extensions.Timer>= 4.2.0
[Function(nameof(TimerFunction))]
[FixedDelayRetry(5, "00:00:10")]
public static void Run([TimerTrigger("0 */5 * * * *")] TimerInfo timerInfo,
FunctionContext context)
{
var logger = context.GetLogger(nameof(TimerFunction));
logger.LogInformation($"Function Ran. Next timer schedule = {timerInfo.ScheduleStatus?.Next}");
}
属性 | 说明 |
---|---|
MaxRetryCount | 必需。 每个函数执行允许的最大重试次数。 -1 表示无限重试。 |
DelayInterval | 重试之间使用的延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
下面是在 function.json 文件中定义的重试策略的示例:
{
"disabled": false,
"bindings": [
{
....
}
],
"retry": {
"strategy": "fixedDelay",
"maxRetryCount": 4,
"delayInterval": "00:00:10"
}
}
可以在重试策略定义上设置这些属性:
properties | 说明 |
---|---|
strategy | 必需。 要使用的重试策略。 有效值为 fixedDelay or exponentialBackoff 进行求值的基于 SQL 语言的筛选器表达式。 |
maxRetryCount | 必需。 每个函数执行允许的最大重试次数。 -1 表示无限重试。 |
delayInterval | 使用 fixedDelay 策略时重试之间的延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
minimumInterval | 使用 exponentialBackoff 策略时的最小重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
maximumInterval | 使用 exponentialBackoff 策略时的最大重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
为触发器定义重试策略的方式取决于 Node.js 版本。
下面是使用固定延迟重试策略的计时器触发器函数的示例:
const { app } = require('@azure/functions');
app.timer('timerTriggerWithRetry', {
schedule: '0 */5 * * * *',
retry: {
strategy: 'fixedDelay',
delayInterval: {
seconds: 10,
},
maxRetryCount: 4,
},
handler: (myTimer, context) => {
if (context.retryContext?.retryCount < 2) {
throw new Error('Retry!');
} else {
context.log('Timer function processed request.');
}
},
});
为触发器定义重试策略的方式取决于 Node.js 版本。
下面是使用固定延迟重试策略的计时器触发器函数的示例:
import { app, InvocationContext, Timer } from '@azure/functions';
export async function timerTriggerWithRetry(myTimer: Timer, context: InvocationContext): Promise<void> {
if (context.retryContext?.retryCount < 2) {
throw new Error('Retry!');
} else {
context.log('Timer function processed request.');
}
}
app.timer('timerTriggerWithRetry', {
schedule: '0 */5 * * * *',
retry: {
strategy: 'fixedDelay',
delayInterval: {
seconds: 10,
},
maxRetryCount: 4,
},
handler: timerTriggerWithRetry,
});
可以在重试策略定义上设置这些属性:
properties | 说明 |
---|---|
strategy | 必需。 要使用的重试策略。 有效值为 fixedDelay or exponentialBackoff 进行求值的基于 SQL 语言的筛选器表达式。 |
maxRetryCount | 必需。 每个函数执行允许的最大重试次数。 -1 表示无限重试。 |
delayInterval | 使用 fixedDelay 策略时重试之间的延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
minimumInterval | 使用 exponentialBackoff 策略时的最小重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
maximumInterval | 使用 exponentialBackoff 策略时的最大重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
下面是使用固定延迟重试策略的计时器触发器函数的示例:
import logging
from azure.functions import AuthLevel, Context, FunctionApp, TimerRequest
app = FunctionApp(http_auth_level=AuthLevel.ANONYMOUS)
@app.timer_trigger(schedule="*/1 * * * * *", arg_name="mytimer",
run_on_startup=False,
use_monitor=False)
@app.retry(strategy="fixed_delay", max_retry_count="3",
delay_interval="00:00:01")
def mytimer(mytimer: TimerRequest, context: Context) -> None:
logging.info(f'Current retry count: {context.retry_context.retry_count}')
if context.retry_context.retry_count == \
context.retry_context.max_retry_count:
logging.info(
f"Max retries of {context.retry_context.max_retry_count} for "
f"function {context.function_name} has been reached")
else:
raise Exception("This is a retryable exception")
可以在重试策略定义上设置这些属性:
properties | 说明 |
---|---|
strategy | 必需。 要使用的重试策略。 有效值为 fixed_delay or exponential_backoff 进行求值的基于 SQL 语言的筛选器表达式。 |
max_retry_count | 必需。 每个函数执行允许的最大重试次数。 -1 表示无限重试。 |
delay_interval | 使用 fixed_delay 策略时重试之间的延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
minimum_interval | 使用 exponential_backoff 策略时的最小重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
maximum_interval | 使用 exponential_backoff 策略时的最大重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。 |
@FunctionName("TimerTriggerJava1")
@FixedDelayRetry(maxRetryCount = 4, delayInterval = "00:00:10")
public void run(
@TimerTrigger(name = "timerInfo", schedule = "0 */5 * * * *") String timerInfo,
final ExecutionContext context
) {
context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now());
}
绑定错误代码
与 Azure 服务集成时,错误可能源自底层服务的 API。 以下文章的“异常和返回代码”部分中提供了与特定于绑定的错误相关的信息: