Azure Functions 错误处理和重试

处理 Azure Functions 中的错误对于帮助避免丢失数据、避免遗漏事件以及监视应用程序的运行状况非常重要。 这也是帮助你了解基于事件的触发器的重试行为的重要方式。

本文介绍了错误处理的常规策略和可用的重试策略。

重要

在此功能推出正式版 (GA) 后,我们将终止运行时中对触发器的重试策略支持(计时器、Kafka 和事件中心除外)。 在 2022 年 12 月已终止对所有触发器的预览重试策略支持(除计时器和事件中心以外)。 有关详细信息,请参阅重试部分。

处理错误

Azure 函数中发生的错误可能由以下任何原因引起:

  • 使用内置 Azure Functions 触发器和绑定
  • 调用底层 Azure 服务的 API
  • 调用 REST 终结点
  • 调用客户端库、包或第三方 API

为避免丢失数据或遗漏消息,必须遵循良好的错误处理做法。 本部分介绍了一些推荐的错误处理做法,并提供了指向更多信息的链接。

启用 Application Insights

Azure Functions 与 Application Insights 集成,可用于收集错误数据、性能数据和运行时日志。 应使用 Application Insights 发现并更好地了解函数执行中发生的错误。 若要了解详细信息,请参阅监视 Azure Functions

使用结构化错误处理

捕获和记录错误对于监视应用程序的运行状况至关重要。 任何函数代码的最顶层应包含 try/catch 块。 在 catch 块中,可以捕获并记录错误。 有关绑定可能引发哪些错误的信息,请参阅绑定错误代码

计划重试策略

多个 Functions 绑定扩展提供对重试的内置支持。 此外,运行时支持为计时器、Kafka 和事件中心触发的函数定义重试策略。 若要了解有关详细信息,请参阅重试。 对于不提供重试行为的触发器,你可能想要实现自己的重试方案。

幂等性设计

处理数据时发生错误可能会给函数带来问题,尤其是在处理消息时。 必须考虑错误发生时会发生什么以及如何避免重复处理。 若要了解有关详细信息,请参阅针对完全相同的输入设计 Azure Functions

重试

有两种可用于函数的重试:

  • 单个触发器扩展的内置重试行为
  • Functions 运行时提供的重试策略

下表指示哪些触发器支持重试以及配置重试行为的位置。 它还链接到有关来自底层服务的错误的详细信息。

触发器/绑定 重试源 配置
Azure Cosmos DB 重试策略 函数级
Azure Blob 存储 绑定扩展 host.json
Azure 事件网格 绑定扩展 事件订阅
Azure 事件中心 重试策略 函数级
Azure 队列存储 绑定扩展 host.json
RabbitMQ 绑定扩展 死信队列
Azure 服务总线 绑定扩展 死信队列
计时器 重试策略 函数级
Kafka 重试策略 函数级

重试策略

从 Azure Functions 运行时版本 3.x 开始,可以为 Functions 运行时强制执行的计时器、Kafka、事件中心和 Azure Cosmos DB 触发器定义重试策略。

重试策略指示运行时重新运行失败的执行,直到成功完成或达到最大重试次数。

当计时器、Kafka、事件中心或 Azure Cosmos DB 触发的函数引发未捕获的异常时,会评估重试策略。 最佳做法是捕获代码中的所有异常,并再次引发你想要导致重试的任何错误。

重要

在执行的重试策略完成之前,不会写入事件中心检查点。 由于此行为,在完成当前批次之前,会暂停特定分区上的进度。

对于 Functions 主机与事件中心之间的交互,事件中心 v5 扩展支持其他重试功能。 有关详细信息,请参阅 host.json 文件“事件中心”部分中的 clientRetryOptions

重试策略

可以配置策略支持的两种重试策略:

允许在指定的时间过后再进行每次重试。

最大重试计数

可以在最终失败之前配置重试函数执行的最大次数。 当前重试计数存储在实例的内存中。

实例可能在每两次重试之间发生故障。 当实例在重试策略实施过程中发生故障时,重试计数会丢失。 出现实例失败时,事件中心触发器能够恢复处理,在新实例上重试该批次,并将重试计数重置为零。 计时器触发器不会在新实例上恢复。

此行为意味着系统只能尽力完成最大重试次数。 在某些罕见情况下,重试某个执行的次数可能会超过请求的最大次数。 对于计时器触发器,重试次数可能会小于请求的最大次数。

重试示例

以下 NuGet 包支持函数级重试:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    public static class TimerFunction
    {
        //<docsnippet_fixed_delay_retry_example>
        [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}");
        }
        //</docsnippet_fixed_delay_retry_example>
    }
}
属性 说明
MaxRetryCount 必需。 每个函数执行允许的最大重试次数。 -1 表示无限重试。
DelayInterval 在重试之间使用的延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。

下面是 function.json 文件中的重试策略:

{
    "disabled": false,
    "bindings": [
        {
            ....
        }
    ],
    "retry": {
        "strategy": "fixedDelay",
        "maxRetryCount": 4,
        "delayInterval": "00:00:10"
    }
}
“function.json”属性 说明
strategy 必需。 要使用的重试策略。 有效值为 fixedDelay or exponentialBackoff进行求值的基于 SQL 语言的筛选器表达式。
maxRetryCount 必需。 每个函数执行允许的最大重试次数。 -1 表示无限重试。
delayInterval 使用 fixedDelay 策略时在重试之间使用的延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。
minimumInterval 使用 exponentialBackoff 策略时的最小重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。
maximumInterval 使用 exponentialBackoff 策略时的最大重试延迟。 将其指定为一个格式为 HH:mm:ss 的字符串。

以下是在函数中使用重试上下文的 Python 示例:

import azure.functions
import logging


def main(mytimer: azure.functions.TimerRequest, context: azure.functions.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.warn(
            f"Max retries of {context.retry_context.max_retry_count} for "
            f"function {context.function_name} has been reached")

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

后续步骤