你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
了解如何使用 Azure Functions 和 Azure 事件中心触发器构建可靠的无服务器解决方案。 本文介绍检查点、错误处理和实现断路器模式的最佳做法,以确保不会丢失任何事件,并且事件驱动的应用程序保持稳定且可复原。
分布式系统中事件流的挑战
假设系统以每秒 100 个事件的常量速率发送事件。 在此速率下,多个并行实例可以在几分钟的时间内处理每秒传入的 100 个事件。
但是,需考虑使用事件流时的以下挑战:
- 事件发布者发送了损坏的事件。
- 函数代码遇到未经处理的异常。
- 下游系统脱机并阻止事件处理。
与在处理过程中锁定消息的 Azure 队列存储触发器不同,Azure 事件中心从流中的单个点读取每个分区。 此读取行为更像是视频播放器,可提供高吞吐量、多个使用者组和重播能力所需的优势。 事件可以从检查点向前或向后读取,但必须移动指针来处理新事件。 有关详细信息,请参阅事件中心文档中的 检查点 。
当流中发生错误且选择不推进指针时,将阻止进一步的事件处理。 换句话说,如果你停止指针为了解决单个事件的处理问题,未处理的事件就会开始堆积起来。
无论成功与否,Functions 都会通过递进流的指针来避免死锁。 由于指针不断向前推进,函数需要适当地处理故障。
事件中心触发器如何使用事件
Azure Functions 通过以下步骤循环从事件中心使用事件:
- 为事件中心的每个分区在 Azure 存储中创建并保留一个指针。
- 新事件会批量接收(默认情况下),主机尝试触发函数并提供该批事件供处理。
- 当函数完成执行(不例外)时,指针是高级的,检查点将保存到默认主机存储帐户。
- 如果条件阻止函数执行完成,主机无法推进指针。 当指针无法前进时,后续执行将重新处理相同的事件。
此行为揭示了一些要点:
未经处理的异常可能会导致事件丢失:
在引发异常时,函数执行仍将继续推进指针。 设置 重试策略 或其他重试逻辑会延迟指针的推进,直到整个重试完成。
函数保证 至少传递一次 :
你的代码和依赖系统可能需要考虑到同一事件可以两次处理的事实。 有关详细信息,请参阅针对完全相同的输入设计 Azure Functions。
处理异常
虽然所有函数代码都应在最高级别的代码中包含 try/catch 块 ,但对于使用事件中心事件的函数,拥有 catch
块更为重要。 这样,在引发异常时,catch 块会在指针递进之前处理错误。
重试机制和策略
由于云中的许多异常是暂时性的,因此错误处理的第一步始终是重试作。 可以应用内置重试策略或定义自己的重试逻辑。
重试策略
Functions 为事件中心提供内置的重试策略。 使用重试策略时,只需引发新的异常,主机会尝试根据定义的策略再次处理事件。 此重试行为需要 5.x 或更高版本的事件中心扩展。 有关详细信息,请参阅重试策略。
自定义重试逻辑
还可以在函数本身中定义自己的重试逻辑。 例如,可以实施遵循以下规则演示的工作流的策略:
- 尝试处理事件三次(可能重试之间有延迟)。
- 如果所有重试的最终结果为失败,请将事件添加到队列中,以便处理可以在流中继续。
- 随后将处理损坏或未处理的事件。
注释
Polly 是 C# 应用程序的复原和暂时性故障处理库的示例。
非异常错误
有些问题可能在不引发异常的情况下发生。 例如,请考虑请求超时或运行函数的实例崩溃的情况。 如果函数在没有异常时无法完成,偏移指针将不会前进。 如果指针未递进,则在执行失败后运行的任何实例将继续读取相同的事件。 这种机制可提供“至少一次”保证。
保证每个事件至少处理一次意味着可以多次处理某些事件。 函数应用需要注意这种可能性,并且必须围绕 幂等性原则构建。
处理失败状态
你的应用在事件处理中可能能够接受地处理一些错误。 但是,还应准备好处理永久性故障状态,这可能是下游处理失败导致的。 在此类故障状态(例如下游数据存储处于脱机状态)中,函数应停止对事件触发,直到系统达到正常状态。
断路器设计模式
实现 断路器 模式时,应用可以有效地暂停事件处理,然后在解决问题后稍后恢复它。
在事件流进程中实现断路器需要两个组件:
- 在所有实例之间共享状态,用于跟踪和监视线路的运行状况。
- 一个可以管理线路状态的主要进程,该状态可以是
open
或者closed
。
实现细节可能会有所不同,但要在多个实例之间共享状态,您需要一种存储机制。 可以在 Azure 存储、Redis 缓存或任何其他可由函数应用实例访问的持久服务中存储状态。
Durable Functions 和 Azure 逻辑应用都提供基础结构来管理工作流和线路状态。 本文介绍如何使用逻辑应用暂停和重启函数执行,从而提供实现断路器模式所需的控制。
跨实例定义故障阈值
当多个实例同时处理事件时,需要保留的共享外部状态来监视线路的运行状况。 然后,可以根据指示失败状态的规则监视此持久状态,例如:
当所有实例在 30 秒内发生超过 100 次事件故障时,断开电路以停止对新事件的触发。
此监视逻辑的实现详细信息因特定应用需求而异,但一般情况下,必须创建一个系统:
- 将失败记录到持久存储。
- 在记录新失败时检查滚动计数,以确定是否满足事件失败阈值。
- 满足此阈值时,发出一个事件,告知系统中断线路。
使用 Azure 逻辑应用管理线路状态
Azure 逻辑应用附带了用于不同服务、功能和有状态业务流程的内置连接器,这是管理线路状态的自然选择。 检测线路何时必须中断后,可以生成逻辑应用来实现此工作流:
- 触发停止函数处理的事件网格工作流。
- 发送包含重启工作流的选项的通知电子邮件。
若要了解如何使用应用设置禁用和重新启用特定函数,请参阅 如何禁用 Azure Functions 中的函数。
电子邮件收件人可以调查线路的运行状况,并在适当情况下通过通知电子邮件中的链接重启线路。 当工作流重启函数时,将从最后一个事件中心检查点处理事件。
使用此方法时,不会丢失任何事件、按顺序处理事件,并且可以根据需要中断线路。
事件网格触发器的迁移策略
在区域之间或某些计划之间迁移现有函数应用时,必须在迁移过程中重新创建应用。 在这种情况下,在迁移过程中,你可能有两个应用能够从同一事件流使用,并写入到同一输出目标。
应考虑在迁移过程中 使用使用者组 来避免事件数据丢失或重复:
为新目标应用创建新的使用者组。
将新应用中的触发器配置为使用此新的使用者组。
这允许两个应用在验证期间独立处理事件。
验证新应用是否正确处理事件。
停止原始应用或删除其订阅/使用者组。