你当前正在访问 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 包支持函数级重试:

[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。 以下文章的“异常和返回代码”部分中提供了与特定于绑定的错误相关的信息:

后续步骤