使用 Azure Functions 和事件中心进行可靠的事件处理

了解如何使用 Azure Functions 和 Azure 事件中心触发器构建可靠的无服务器解决方案。 本文介绍检查点、错误处理和实现断路器模式的最佳做法,以确保不会丢失任何事件,并且事件驱动的应用程序保持稳定且可复原。

分布式系统中事件流的挑战

假设系统以每秒 100 个事件的常量速率发送事件。 在此速率下,很快就可以由多个并行实例每秒处理传入的 100 个事件。

但是,请考虑以下使用事件流的挑战:

  • 事件发布者发送已损坏的事件。
  • 函数代码遇到未经处理的异常。
  • 下游系统脱机并阻止事件处理。

与在处理过程中锁定消息的 Azure 队列存储触发器不同,Azure 事件中心从流中的单个点读取每个分区。 此读取行为更像是视频播放器,可提供高吞吐量、多个使用者组和重播能力所需的优势。 事件可以从检查点向前或向后读取,但必须移动指针以处理新事件。 有关详细信息,请参阅事件中心文档中的 检查点

当流中出现错误且您选择不移动指针时,将阻止对后续事件的处理。 换句话说,如果您停止游标以处理事件处理中的一个问题,未处理的事件将开始堆积。

无论成功还是失败,函数始终推进流的指针,从而避免死锁。 由于指针不断向前推进,函数需要适当地处理故障。

事件中心触发器如何消费事件

Azure Functions 通过循环执行以下步骤,从事件中心消费事件:

  1. 为事件中心的每个分区在 Azure 存储中创建并保留一个指针。
  2. 默认情况下,新事件以批量方式接收,然后主机会尝试触发一个函数来处理这批事件。
  3. 当函数完成执行(不例外)时,指针是高级的,检查点将保存到默认主机存储帐户。
  4. 如果条件阻止函数执行完成,主机无法推进指针。 当指针无法前进时,后续执行将重新处理相同的事件。

此行为揭示了一些要点:

  • 未经处理的异常可能会导致事件丢失。

    引发异常的函数执行将继续推进指针位置。 设置 重试策略 或其他重试逻辑会延迟指针前进,直到整个重试完成。

  • 函数保证 至少传递一次

    你的代码和依赖系统可能需要考虑到同一事件可以两次处理的事实。 有关详细信息,请参阅 为相同的输入设计 Azure 函数

处理异常

虽然所有函数代码都应在最高级别的代码中包含 try/catch 块 ,但对于使用事件中心事件的函数,拥有 catch 块更为重要。 这样,当引发异常时,catch 代码块会在指针前进之前处理该错误。

重试机制和策略

由于云中的许多异常是暂时性的,因此错误处理的第一步始终是重试作。 可以应用内置重试策略或定义自己的重试逻辑。

重试策略

Functions 为事件中心提供内置的重试策略。 使用重试策略时,只需引发新的异常,主机会尝试根据定义的策略再次处理事件。 此重试行为需要 5.x 或更高版本的事件中心扩展。 有关详细信息,请参阅重试策略

自定义重试逻辑

还可以在函数本身中定义自己的重试逻辑。 例如,可以实施遵循以下规则演示的工作流的策略:

  • 尝试处理事件三次(可能重试之间有延迟)。
  • 如果所有重试的最终结果为失败,请将事件添加到队列中,以便处理可以在流中继续。
  • 随后会处理损坏或未处理的事件。

备注

Polly 是 C# 应用程序的复原和暂时性故障处理库的示例。

非异常错误

可能会出现一些问题,而不会引发异常。 例如,请考虑请求超时或运行函数的实例崩溃的情况。 如果一个函数在未发生异常的情况下无法完成,则偏移指针永远不会前进。 如果指针未推进,则在执行失败后运行的任何实例将会继续读取相同的事件。 这种情况提供至少一次的保证。

保证每个事件至少处理一次意味着可以多次处理某些事件。 函数应用需要注意这种可能性,并且必须围绕 幂等性原则构建。

处理失败状态

你的应用在事件处理中可能能够接受地处理一些错误。 但是,还应准备好处理永久性故障状态,这可能是下游处理失败导致的。 在此类故障状态(例如下游数据存储处于脱机状态)中,函数应停止对事件触发,直到系统达到正常状态。

断路器设计模式

实现 断路器 模式时,应用可以有效地暂停事件处理,然后在解决问题后稍后恢复它。

在事件流进程中实现断路器需要两个组件:

  • 在所有实例之间共享状态,用于跟踪和监视线路的运行状况。
  • 一个可以管理线路状态的主要进程,可以是openclosed

实现细节可能会有所不同,但为了在各个实例之间共享状态,你需要一个存储机制。 可以在 Azure 存储、Redis 缓存或任何其他可由函数应用实例访问的持久服务中存储状态。

Durable FunctionsAzure 逻辑应用都提供基础结构来管理工作流和线路状态。 本文介绍如何使用逻辑应用暂停和重启函数执行,从而提供实现断路器模式所需的控制。

跨实例定义故障阈值

当多个实例同时处理事件时,需要保留的共享外部状态来监视线路的运行状况。 然后,可以根据指示失败状态的规则监视此持久状态,例如:

在所有实例的 30 秒内发生超过 100 个事件故障时,断开电路以停止对新的事件进行触发。

此监视逻辑的实现详细信息因特定应用需求而异,但一般情况下,必须创建一个系统:

  1. 将失败记录到持久存储。
  2. 在记录新失败时检查滚动计数,以确定是否满足事件失败阈值。
  3. 满足此阈值时,发出一个事件,告知系统中断线路。

使用 Azure 逻辑应用管理线路状态

Azure 逻辑应用附带了用于不同服务、功能和有状态业务流程的内置连接器,这是管理线路状态的自然选择。 检测线路何时必须中断后,可以生成逻辑应用来实现此工作流:

  1. 触发停止函数处理的事件网格工作流。
  2. 发送包含重启工作流的选项的通知电子邮件。

若要了解如何使用应用设置禁用和重新启用特定函数,请参阅 如何禁用 Azure Functions 中的函数

电子邮件收件人可以调查线路的运行状况,并在适当情况下通过通知电子邮件中的链接重启线路。 当工作流重启函数时,将从最后一个事件中心检查点处理事件。

使用此方法时,不会丢失任何事件、按顺序处理事件,并且可以根据需要中断线路。

注意:作者在 AI 的帮助下创作了此文章。 了解详细信息