企业应用的事件驱动架构与消息队列:快速、抗峰值、易扩展
当顾客在网店点击支付按钮时,需要发生许多事情:订单被记录、库存被扣减、发票被创建、确认邮件被发送、仓库收到通知、积分被增加,数据进入报表。在设计简单的应用中,所有这些步骤在同一个流程里按顺序完成。订单少的时候,看起来没有问题。但一旦某一步变慢,比如邮件服务器卡住,整个订单都被拖住,顾客盯着转圈的屏幕,不知道付款是否成功。
事件驱动架构和消息队列,就是让这些步骤不再互相“绑架”的应用设计方式。本文不堆砌术语,解释这些概念、该方法在企业应用中何时值得采用、常见模式、需要管理的风险,以及如何在不重写整个系统的前提下逐步起步。
摘要
- 消息队列把必须立刻完成的工作与可以稍后跟进的工作分开,使用户不必等待次要步骤
- 事件驱动架构让模块通过事件互相通知,而不是直接相互调用,因而耦合更松散
- 主要好处:抗负载峰值的韧性、故障被隔离,以及便于增加新功能
- 代价:复杂度、数据一致性的延迟,以及对更好监控的需求
- 从一个明确有问题的流程开始,例如通知或与其他系统的同步,而不是全面改造
顺序执行的流程有什么问题
在同步设计中,一次用户请求会触发一连串工作,全部完成后才返回答复。这种模式简单,但会带来一些常见问题。
- 用户感觉慢:等待时间是所有步骤之和,包括其实无需等待的步骤,例如发送邮件
- 对故障脆弱:如果某个第三方服务宕机,即使核心工作已经成功,整个流程也会失败
- 难以承受峰值:在营销活动或促销期间,成千上万的请求堆积在最薄弱的环节,拖垮整个应用。这与[大促前的负载测试](/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、网店到第三方服务,包括故障处理与监控。如果您的应用在繁忙时经常卡顿,或系统间的同步不可靠,欢迎与我们探讨解决方案。
免费清单:如何选择软件公司
签约前评估供应商的 24 个要点,可保存为 PDF。