你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

可复原的事件中心和函数设计

可以采取的一些关键措施包括:错误处理、幂等性设计和管理重试行为,此类设置可确保事件中心触发的函数具有复原能力并能够处理大量数据。 本文介绍这些关键概念,并针对无服务器事件流式处理解决方案提出建议。

Azure 提供了三种可与 Azure Functions 配合使用的主要消息传递服务,以支持各种独特的事件驱动方案。 由于 Azure 事件中心具有分区式使用者模型并能以高速率引入数据,因此通常用于事件流式处理和大数据方案。 有关 Azure 消息传送服务的详细比较,请参阅在 Azure 消息传送服务之间进行选择 - 事件网格、事件中心和服务总线

流式处理的优势和挑战

了解流的优缺点有助于了解事件中心等服务的运行方式。 在做出重要的体系结构决策、排查问题和优化性能时,也需要用到此上下文。 请考虑以下有关涉及事件中心和 Functions 的解决方案的关键概念:

  • 流不是队列:基于分区式使用者模型构建的事件中心、Kafka 和其他类似产品/服务在本质上不支持消息代理(如 服务总线)中的部分主要功能。 最能说明该问题的就是非破坏性读取的这一点。 非破坏性读取表示 Functions 主机所读取的数据不会在之后被删除。 与之相对的是,消息是不可变的并且可供其他使用者读取,包括可能再次读取它的同一客户。 因此,实现使用者竞争等模式的解决方案更适合传统消息代理。

  • 缺少固有死信支持:死信通道不是事件中心或 Kafka 中的固有功能。 通常,死信的概念集成在流式处理解决方案中,用于表示无法处理的数据。 此功能不是事件中心中的固有元素,仅在使用者端添加,用于实现类似的行为或效果。 如果需要死信支持,则可能需要查看所选的流式处理消息服务。

  • 工作以分区为单位:在传统消息代理中,工作以单个消息为单位。 在流式处理解决方案中,工作通常以分区为单位。 如果事件中心中的每个事件被视为一条离散消息,需要将其视为订单处理操作或财务事务,则很可能是使用了错误的消息传递服务。

  • 无服务器端筛选:事件中心能够实现巨大规模和吞吐量的原因之一是该服务本身的开销较低。 服务器端筛选、索引和跨代理协调等功能不属于事件中心体系结构。 Functions 有时用于通过根据正文或标头中的内容将事件路由到其他事件中心来筛选事件。 此方法在事件流式处理中很常见,但需要注意的是,每个事件都由初始函数读取和评估。

  • 每个读取者都必须读取所有数据:由于服务器端筛选不可用,因此使用者按顺序读取分区中的所有数据。 这包括可能不相关甚至格式错误的数据。 有几个选项甚至策略可用于解决这些难题,本节稍后将对此进行介绍。

这些重要的设计决策使事件中心能够执行其核心工作:支持大量传入的事件,并提供可靠且可复原的服务供使用者从中读取内容。 每个使用者应用程序都负责维护其自己的客户端偏移或指向这些事件的游标。 低廉的费用使事件中心在事件流式处理方面成为经济实惠且功能强大的选择。

幂等性

Azure 事件中心的核心原则之一是“至少传递一次”的概念。 此方法可确保事件始终会被传递。 这也意味着使用者(如函数)可以多次接收事件,甚至重复接收。 因此,事件中心触发的函数支持幂等使用者模式非常重要。

在“至少一次”传递的假设下进行操作(尤其是在事件驱动体系结构的上下文中),是可靠处理事件的一种负责任的方法。 函数必须为幂等函数,这样多次处理同一事件的结果可以与处理一次的结果相同。

重复的事件

有几种不同的情况可能导致将重复事件传递到函数:

  • 检查点:如果 Azure Functions 主机崩溃,或者未达到为批处理检查点频率设置的阈值,则不会创建检查点。 因此,使用者的偏移量不会提前,在下次调用函数时,它会从最后一个检查点恢复。 请务必注意,创建检查点的操作是在每个使用者的分区级别进行的。

  • 发布的重复事件:有许多技术可以降低将同一事件发布到流的可能性,但是,使用者仍有责任以幂等方式处理重复项。

  • 缺少确认:在某些情况下,对服务的传出请求成功以后可能收不到来自服务的确认 (ACK)。 这可能会导致我们误认为传出调用失败,并启动一系列重试操作或从函数获取其他结果。 最后,可能会发布重复事件,或者不创建检查点。

重复数据删除技术

相同输入设计函数应是与事件中心触发器绑定配对时采用的默认方法。 应考虑采用以下技术:

  • 查找重复项:在进行处理之前,请执行必要的步骤来验证是否应处理该事件。 在某些情况下,需要进行调查才能确认事件是否仍然有效。 也有可能存在数据时效性方面的问题或让事件无效的逻辑,导致不再需要处理该事件。

  • 设计幂等性事件:通过在事件的有效负载中提供其他信息,可以确保多次处理事件不会产生任何不利影响。 以包含从银行帐户提取金额的事件为例。 如果不进行认真处理,那么可能会多次扣除帐户的余额。 但是,如果同一事件包含帐户的更新余额,则它可用于对银行帐户余额执行更新插入操作。 这种事件传递状态转移方法有时需要在生成方和使用方之间进行协调,并且应在对参与的服务有帮助时使用。

错误处理和重试

错误处理和重试是分布式事件驱动应用程序的一些最重要的特性,Functions 也不例外。 对于事件流式处理解决方案,适当的错误处理支持至关重要,因为如果处理不当,成千上万的事件很快就会变成相同数量的错误。

有关错误处理的指导

如果不进行错误处理,那么可能很难实现重试、检测运行时异常以及调查问题。 每个函数都应至少具备某种程度的错误处理能力。 一些建议的准则如下:

  • 使用 Application Insights:启用并使用 Application Insights 来记录错误并监视函数的运行状况。 请注意处理大量事件的方案的可配置采样选项。

  • 添加结构化错误处理:为每种编程语言应用合适的错误处理构造,以捕获、记录和检测函数代码中预期的和未经处理的异常。 例如,在 C#、Java 和 JavaScript 中使用 try/catch 块,并利用 Python 中的 try 和 except 块来处理异常。

  • 日志记录:在执行期间捕获异常,从而能有机会记录关键信息,这些信息可用于可靠地检测、重现和修复问题。 记录异常,不仅记录消息,还记录正文、内部异常和其他之后有用的项目。

  • 请勿捕获但忽略异常:最糟糕的一种情况是捕获了异常,却对其置之不理。 如果捕获到常规异常,请将其记录至适当位置。 如果不记录错误,则很难对 bug 和报告的问题进行调查。

重试

在事件流式处理体系结构中实现重试逻辑可能比较复杂。 该过程中存在很多增加难度的注意事项,例如支持取消令牌、重试计数和指数退避策略。 好在 Functions 提供了重试策略,这些策略可以解决通常需要自行编码的许多任务。

将重试策略与事件中心绑定配合使用时必须考虑的几个重要因素包括:

  • 避免无限重试:当最大重试次数设置为 -1 时,函数将无限重试。 通常,应谨慎地对 Functions 采用无限重试,并且几乎从不对事件中心触发器绑定采用无限重试。

  • 选择适当的重试策略:对于受到来自其他 Azure 服务的反压力的情况,固定延迟策略可能是最佳选择。 在这些情况下,延迟有助于避免这些服务遇到网络带宽限制和其他限制。 指数回退策略为重试延迟间隔提供了更大的灵活性,并且通常在与第三方服务、REST 终结点和其他 Azure 服务集成时使用。

  • 保持较低的间隔和重试次数:尽可能将重试间隔维持在一分钟以内。 此外,请将最大重试尝试次数保持在合理的较低值。 在 Functions 消耗计划中运行时,这些设置尤为必要。

  • 断路器模式:预期会不时出现暂时性故障错误,并且是重试的自然用例。 但是,如果在处理函数期间出现大量故障或问题,合理的做法是先停止函数,然后解决问题并在之后重启。

对于 Functions 中的重试策略,我们需要明白它是重新处理事件的最佳功能。 它不能替代错误处理、日志记录和其他为代码提供复原能力的重要模式。

适用于故障和损坏数据的策略

有几种值得注意的方法可用于处理由于事件流中的故障或错误数据而导致的问题。 一些基本策略包括:

  • 停止进行发送和读取:暂停事件的读取和写入操作,以修复基础问题。 此方法的优点是不会丢失数据,并且可以在推出修补程序后恢复操作。此方法可能需要在体系结构中采用断路器组件,并可能需要向受影响的服务发出通知来实现暂停。 在某些情况下,可能需要在问题解决之前停止函数。

  • 删除消息:如果消息不重要或被视为非任务关键消息,请考虑在不处理消息的情况下继续操作。 这不适用于需要高度一致性的方案,例如在国际象棋比赛中记录走棋或金融类交易。 建议在函数内进行错误处理,以便捕获和删除无法处理的消息。

  • 重试:在很多情况下,可能需要对事件进行重新处理。 最常见的是在调用其他服务或依赖项时遇到暂时性错误的情况。 网络错误、服务限制和可用性以及高度一致性可能是最常见的有理由重新处理尝试的用例。

  • 死信:此处的理念是将事件发布到不同的事件中心,从而让现有流不中断。 鉴于它已经脱离了热路径,可以稍后或通过不同的进程来处理。 此解决方案经常用于处理有害消息或事件。 应注意的是,配置了不同使用者组的每个函数在其流中仍会遇到错误或损坏的数据,并且必须认真处理这些数据。

  • 重试和死信:另一种常见的方法是结合多次重试尝试,然后最终在达到阈值时发布到死信流。

  • 使用架构注册表:架构注册表可用作帮助提高一致性和数据质量的主动工具。 随着架构的发展,Azure 架构注册表可以支持架构的转换和版本控制以及不同的兼容模式。 从其核心来看,架构将充当生成者和使用者之间的协定,这可以减少将无效或损坏数据发布到流的可能性。

归根结底,不存在完美的解决方案,我们需要对每个策略的效果与涉及的权衡进行全面的研究。 根据具体的要求,将其中几种技术结合使用应该才会得出最佳方法。

作者

本文由 Microsoft 维护, 它最初是由以下贡献者撰写的。

主要作者:

若要查看非公开的 LinkedIn 个人资料,请登录到 LinkedIn。

后续步骤

在继续操作之前,请考虑查看以下相关文章: