使用 Inbox 模式实现幂等性

不久前,我写过关于 Outbox 模式 的文章。现在,是时候谈谈它不可或缺的搭档:Inbox 模式了。

理解消息投递保证

在深入探讨 Inbox 模式之前,我们先明确分布式系统中三种核心的消息投递语义:

最多一次(At-most-once)

在此模型中,消息最多被投递一次——可能成功,也可能丢失,但绝不会重复。该模型没有确认(ACK)机制,如果消息因崩溃、超时或网络问题未能被处理,就会直接丢失。

这种语义常见于轻量级或内存中的消息系统。例如,NATS(在其核心模式下)默认就采用最多一次的投递方式。

权衡点:实现简单,但可靠性较低——除非发送方自行重试,否则消息可能永久丢失。

至少一次(At-least-once)

该模型保证消息至少会被投递一次。如果消费者未发送 ACK,消息代理(broker)会不断重试投递——可能多次。

这是大多数生产级消息中间件的默认行为,比如 RabbitMQAWS SQSRocketMQ,以及 Kafka(在关闭自动提交 offset 的情况下)。

权衡点:你必须确保消息处理器是幂等的,因为重复投递不可避免——尤其是在处理时间超过可见性超时或 ACK 超时时。

恰好一次(Exactly-once)

这是消息投递的“圣杯”:每条消息恰好被处理一次,既无丢失,也无重复。

尽管 Kafka(配合幂等生产者和事务型消费者)等系统声称支持“恰好一次”语义,但在异构服务之间实现端到端的真正“恰好一次”投递极其困难。它通常需要消息层与应用逻辑紧密协作(例如通过事务性写入和去重机制)。

现实情况:大多数系统实际上是通过“至少一次投递 + 幂等处理”来模拟“恰好一次”——而这正是 Inbox 模式 大放异彩的地方。

问题:可靠消费的挑战

让我们聚焦于广泛使用的“至少一次”投递语义。实践中常遇到两大挑战:

  1.  处理缓慢导致重复:某条消息处理时间很长。单纯延长可见性超时并不总是可行。一旦处理时间超过超时阈值,消息代理就会重新投递,导致业务逻辑被重复执行。
  2. 流式消费中的“毒丸”问题:在 Kafka 等流式系统中,一条处理失败的消息(毒丸)可能阻塞整个分区。在解决该消息前,你无法继续消费后续消息,导致整个应用停滞。

如何在这些场景下实现幂等且具有弹性的消息处理?答案就是 **Inbox 模式**。

解法:Inbox 模式

Inbox 模式提供了一种实现幂等消费的机制。其核心思想是:将数据库作为可靠、权威的入站消息日志。

具体工作流程如下:

  1. 收到消息后,第一步是在数据库(如 PostgreSQL、MySQL)中一个专用的 inbox 表中持久化该消息,且该操作与任何初始业务数据变更处于同一个数据库事务中。
  2. 消息记录包含一个唯一标识(如 message_id)。
  3. 在处理消息前,系统先查询 inbox 表,检查该 ID 的消息是否已被处理(或正在处理)。
  4. 如果是重复消息,则直接确认(ACK)并忽略。

这种方式优雅地解决了前述问题:

  • 针对问题 #1(重复投递):inbox 表充当幂等性过滤器。即使同一条消息被多次投递,也只会被处理一次。
  • 针对问题 #2(毒丸阻塞):在流式系统中,你可以先 ACK 有问题的消息以解除队列阻塞。待问题修复后,重置流或偏移量进行重放。Inbox 模式会自动跳过之前已成功处理的消息。

权衡与考量

没有银弹。Inbox 模式也带来了一些代价:

  • 性能开销:每条消息都需要一次数据库写入和一次查询,增加了延迟。
  • 架构复杂性:需额外管理 inbox 表和后台处理器。
  • 数据库瓶颈:在极高吞吐量下,数据库可能成为性能瓶颈。
  • 存储开销:inbox 表可能迅速膨胀,需制定保留策略或归档机制。

工具与实现

好消息是,Inbox 模式已被广泛支持。你很可能在所用编程语言的生态中找到现成框架。若没有,其核心逻辑也相对简单,可自行实现。

对于 .NET 开发者,Brighter 项目对 Inbox 和 Outbox 模式提供了优秀的内置支持。

结语

Inbox 模式是构建弹性事件驱动系统的重要工具。通过将数据库作为保护层,它确保了幂等处理,并能优雅应对各种故障场景。尽管引入了一定复杂性,但对关键业务流程而言,这种“无重复、不丢失”的处理保障价值巨大。当你使用 Outbox 模式确保可靠发送时,请务必搭配 Inbox 模式以实现可靠消费。

参考资料

- https://event-driven.io/en/outbox_inbox_patterns_and_delivery_guarantees_explained/  
- https://newsletter.systemdesignclassroom.com/p/every-outbox-needs-an-inbox

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值