跳转到主要内容
AG·SORA
技术

企业应用的事件驱动架构与消息队列:快速、抗峰值、易扩展

发布于 10 分钟阅读作者:agsora.tech
AG·SORA / BLOGAG·SORA技术企业应用的事件驱动架构与消息队列:快速、抗峰值、易扩展agsora.tech
本文目录

当顾客在网店点击支付按钮时,需要发生许多事情:订单被记录、库存被扣减、发票被创建、确认邮件被发送、仓库收到通知、积分被增加,数据进入报表。在设计简单的应用中,所有这些步骤在同一个流程里按顺序完成。订单少的时候,看起来没有问题。但一旦某一步变慢,比如邮件服务器卡住,整个订单都被拖住,顾客盯着转圈的屏幕,不知道付款是否成功。

事件驱动架构和消息队列,就是让这些步骤不再互相“绑架”的应用设计方式。本文不堆砌术语,解释这些概念、该方法在企业应用中何时值得采用、常见模式、需要管理的风险,以及如何在不重写整个系统的前提下逐步起步。

摘要

  • 消息队列把必须立刻完成的工作与可以稍后跟进的工作分开,使用户不必等待次要步骤
  • 事件驱动架构让模块通过事件互相通知,而不是直接相互调用,因而耦合更松散
  • 主要好处:抗负载峰值的韧性、故障被隔离,以及便于增加新功能
  • 代价:复杂度、数据一致性的延迟,以及对更好监控的需求
  • 从一个明确有问题的流程开始,例如通知或与其他系统的同步,而不是全面改造

顺序执行的流程有什么问题

在同步设计中,一次用户请求会触发一连串工作,全部完成后才返回答复。这种模式简单,但会带来一些常见问题。

  • 用户感觉慢:等待时间是所有步骤之和,包括其实无需等待的步骤,例如发送邮件
  • 对故障脆弱:如果某个第三方服务宕机,即使核心工作已经成功,整个流程也会失败
  • 难以承受峰值:在营销活动或促销期间,成千上万的请求堆积在最薄弱的环节,拖垮整个应用。这与[大促前的负载测试](/blog/load-testing-uji-beban-sebelum-promo-besar-dan-musim-puncak)有关
  • 紧耦合:增加一个新功能,例如向分析系统发送数据,就意味着修改主流程的代码,并有破坏它的风险
  • 不安全的重复:如果用户因为太慢而点了两次按钮,工作可能被重复执行

一个真实的例子:结账流程依次调用支付服务、物流服务、会计系统,然后发邮件。如果会计系统正好维护五分钟,已经付款的顾客会看到错误提示。付款已经发生,订单却显示失败,客服团队只能手工收拾善后。

基本概念:队列、事件与工作进程

只需在概念层面理解几个术语即可。

消息队列

消息队列是为等待处理的工作提供的有序临时存放处。发送方放入一条消息,例如“为订单 1042 发送确认邮件”,然后立即继续自己的任务。另一个工作进程取走该消息,在准备好时处理。如果工作进程繁忙或短暂宕机,消息会安全地等待。

事件

事件是对某件事已经发生这一事实的记录,例如“订单已创建”、“已收到付款”或“库存不足”。用过去时命名强调事件不容争辩;其他各方能做的只是对它作出反应。

生产者与消费者

生产者是应用中宣布事件或把工作放入队列的部分。消费者是监听并作出反应的部分。一个事件可以同时被多个消费者收听:“订单已创建”事件可以触发扣减库存、创建发票和发送通知,而生产者无需知道它们是谁。

消息代理

代理(broker)是维护队列并安排将消息投递给消费者的组件。选择多种多样,从云上托管服务到自行安装的软件。对于大多数中型企业,与自行运维相比,托管服务能减轻运营负担。

数据中心里网线整齐排列的服务器机架
当其他部分繁忙或停止时,消息代理让工作保持安全

企业应用中常用的模式

后台任务

最简单、最常有用的模式:把用户无需等待的工作转移到队列。例如发送邮件和通知、生成大型报表文件、调整上传图片的大小,以及导入数据文件。用户立即得到“已受理”的响应,而处理在后台进行。

发布与订阅

宣布一个事件,多方订阅。这一模式适合相互依赖的 ERP 模块。当销售被记录时,库存、财务和 CRM 模块各自作出反应。增加新的消费者,例如分析模块,不会改变生产者。

系统间同步

把数据变更从一个系统复制到另一个系统,例如从 ERP 到网店或电商平台,非常适合使用队列。库存变化作为事件发送,如果目标系统不可用,事件会等待并重新投递。此模式补充了Webhook 与实时集成,并与统一实体店与电商平台库存相关。

削峰

流量激增时,请求被暂存于队列,并以系统能承受的速率处理。用户获得最终结果可能稍慢,但应用不会崩溃。

长流程工作流

涉及多个阶段且可能耗时的业务流程,例如分级审批或订单履行,可以建模为一系列事件,并在每一步记录状态。

风险与需要付出的代价

这种方法不是万能药。在决定之前需要了解一些后果。

  • 一致性延迟:订单创建后,库存可能几秒钟后才减少。应用必须设计成在这段间隙内显示仍然合理
  • 重复消息:队列系统通常保证至少投递一次,因此同一条消息可能到达两次。消费者必须可安全重复执行(幂等),例如记录已处理消息的编号
  • 顺序不一定有保证:如果路径不同,“订单已取消”事件可能先于“订单已创建”到达。设计需要处理顺序或使用版本号
  • 毒消息:总是处理失败的消息会堵塞队列。请使用重试上限和专门的失败消息队列,以便人工检查
  • 难以追踪:一次用户操作会扩散到许多消费者,因此如果没有关联标识和[良好的可观测性](/blog/observabilitas-log-dan-penanganan-error-aplikasi-bisnis),找出根本原因会变得困难
  • 运营成本:多了一个需要监控、保护和备份的组件
  • 测试复杂度:测试异步流程比调用单个函数更难

因此,对于直接处理就足够的事情,不要使用队列。如果一个操作很快,且必须在答复用户之前确认成功,例如校验银行卡或扣减稀缺库存,同步方式通常更简单也更安全。

保持数据正确:幂等与事务

有两项技术实践决定事件驱动设计是否可信。

幂等性

幂等操作即使执行多次,结果也相同。例如,“将订单 1042 标记为已付款”可以安全重复,而“余额增加 100,000”则不能。请把消费者设计为检查某条消息是否已被处理,使用唯一的消息编号或订单编号作为键。同样的原则适用于来自支付服务商的通知,如支付网关集成中所述。

可靠的事件存储

当应用把数据保存到数据库,然后作为两个独立步骤向队列发送消息时,会出现一个微妙的问题。如果应用在两者之间宕机,就会出现数据已保存但消息未发送,或者相反的情况。发件箱(outbox)模式可以解决:消息与数据变更在同一数据库事务中写入一张专用表,再由独立的进程将其转发到队列。这样,数据变更和事件的宣布总是一起进行。

何时值得采用

并非每个应用都需要。以下迹象表明队列和事件会带来价值:

  • 存在缓慢或不可靠的步骤,例如邮件、报表导出或对外部系统的调用,使用户需要等待
  • 季节性或营销活动带来的负载峰值使应用卡顿
  • 多个系统需要知道同一个变更,例如 ERP、网店和分析系统
  • 第三方系统的故障不应导致核心交易失败
  • 您计划在未来增加许多集成,并希望避免代码纠缠

相反,只有几十名用户、流程简单的小型应用,通常无需额外组件反而更好。也要考虑它与单体还是微服务这一决策的关系:队列可以用在整洁的单体应用内部,无需把它拆分成许多服务。

显示带有小型折线图和性能数字的指标面板的屏幕
队列长度和最旧消息的存留时间是需要监控的健康指标

监控队列

不受监控的队列是迟早会出问题的隐患。以下一些指标值得设置告警:

  • 队列长度:等待中的消息数量。持续上升意味着消费者跟不上
  • 最旧消息的存留时间:等待最久的消息已经等了多久。这通常比队列长度更有意义
  • 失败与重试率:激增表明消费者或目标系统出现问题
  • 失败消息队列的内容:其中每条消息都需要审查并决定去向
  • 单条消息处理时间:上升趋势可能意味着性能问题,包括数据库方面,如[数据库与应用变慢](/blog/performa-database-dan-aplikasi-bisnis-yang-melambat)所述
  • 工作进程的资源使用:决定何时需要增加容量

在每条消息上记录关联标识,使一次用户操作可以从头到尾追踪到所有作出反应的消费者。

逐步起步的步骤

  • 找出一个明显令人头疼的流程,例如因邮件而变慢的结账,或经常失败的库存同步
  • 只把次要步骤移到队列,核心步骤保持同步
  • 设计带有唯一标识、版本和适量数据的消息,而不是整个对象
  • 让消费者具备幂等性,并制定重试策略和失败消息队列
  • 在上线之前而不是出问题之后部署监控和告警
  • 测试故障场景:停止消费者、发送重复消息,并模拟缓慢的目标系统
  • 逐步上线,例如借助[功能开关与渐进式发布](/blog/feature-flag-dan-rilis-bertahap-untuk-aplikasi-bisnis),证明稳定后再扩展到其他流程

常见错误

  • 对所有事情都使用队列,包括简单且必须同步的事情
  • 不处理重复消息,导致客户收到两封邮件或被重复收费
  • 任由失败消息堆积而无人检查
  • 在消息中发送大对象,导致数据很快过时或不一致
  • 不包含关联标识,导致追踪问题要花数天
  • 忽视安全:消息可能含有个人数据,因此需要设置访问控制和加密

常见问题

小企业需要消息队列吗?

通常还不需要。请从应用框架提供的后台任务功能开始。只有当业务量、连接的系统数量或韧性要求上升时,独立的代理组件才值得引入。

队列与 Webhook 有什么区别?

Webhook 是一个系统在事情发生时通过 HTTP 调用通知另一个系统的方式。队列是在接收方不可用时保证消息安全的暂存区。二者经常一起使用:收到的 Webhook 先放入队列,再进行处理。

这和微服务是一回事吗?

不是。队列和事件是通信模式,既可用于单体应用,也可用于微服务。许多企业无需拆分应用,就能从队列中获益良多。

消息中的数据安全如何保障?

限制消息中的个人数据,使用标识引用完整数据,设置队列的访问权限,并按需启用传输和存储加密。长期保存的失败消息也需要保留策略。

在一个流程上实施需要多久?

对于通知这样的简单流程,需几天到几周,包括测试和监控。涉及系统间同步或财务数据的流程耗时更长,因为故障测试要求更高。

结论

事件驱动架构和消息队列帮助企业应用在用户眼中保持快速、抵御峰值和故障,并且可以在不拆解主流程的情况下不断成长。代价是需要通过幂等性、失败消息处理和良好监控来管理的额外复杂度。请先在一个明确有问题的流程上小规模起步,衡量结果,证明有用后再扩展。

Agsora 帮助设计和构建系统之间可靠的集成,从 ERP、网店到第三方服务,包括故障处理与监控。如果您的应用在繁忙时经常卡顿,或系统间的同步不可靠,欢迎与我们探讨解决方案。

构建可靠的系统集成

Agsora 设计 ERP、网店与第三方服务之间的集成,使其能够抵御负载峰值与故障。

免费咨询

免费清单:如何选择软件公司

签约前评估供应商的 24 个要点,可保存为 PDF。

获取清单

准备好构建与您业务共同成长的系统了吗?

与 AG·SORA 团队讨论您的需求, 完全免费,无需承诺。