Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >空谈发件箱模式(outbox pattern)

空谈发件箱模式(outbox pattern)

作者头像
Bruce Li
发布于 2020-10-14 09:47:50
发布于 2020-10-14 09:47:50
3.3K01
代码可运行
举报
运行总次数:1
代码可运行

之前聊过很多次分布式事务这个话题,本文继续聊一聊如何用发件箱模式实现分布式事务。

1

基于微服务架构模式(当然不限于)的应用系统,常常会利用消息中间件kafka,rabbitmq等)来实现各个微服务之间的通信。对于用户的某个操作,一个微服务可能需要执行“存数据库”和“发送event”两个步骤。

举个例子,用户创建一个订单,订单服务需要存数据库和发送订单created event,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
begin transactionsave(order) to dbsend(order_created_event) to kafkacommit/rollback

对于上面这个例子,由于牵扯到两个系统,数据库事务和kafka事务并不能保证整个操作的事务性(ACID),至多能保证各个子系统的事务性,最终可能导致出现如下数据不一致的情况:

  1. 发送event成功,然后commit到数据库失败(原因可能是数据库不available,或者是违反数据库表的constraints)。
  2. 发送event成功到commit到数据库之间有一定延迟,如果consumer消费到event,接着call订单服务查找这个order,结果就可能会查不到。

有同学会说,可以把发送event放在存数据库transaction后面,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
begin transactionsave(order) to dbcommit/rollbacksend(order_created_event) to kafka

即使这样,又会出现另外一种不一致的情况,即:存数据库成功,发event失败,比如由于kafka临时不available。这直接导致用户的操作失败,必须要重新提交请求,在某些场景下对用户来说可能是不可接受的;并且数据库里面还有一条脏数据存在。

那么,如何保证存数据库和发event是一个transaction呢?

其实,抽象这个问题,其本质就是一个分布式事务问题,如何实现分布式事务,网上有很多文章介绍,可能最常被提到的就是两阶段提交2PC(2 phase commit),但其需要各个子系统都支持两阶段提交协议(比如XA),很多数据库都有支持,但是很多消息中间件都不支持,所以2PC不适用这里的场景,并且由于2PC的一些缺点(参见我的另外一篇文章:关于分布式系统数据一致性的那些事(二)),它并不是实现分布式事务的常规选择。

基于此,这里介绍的发件箱模式可以有效地解决这个问题。

2

发件箱模式,简单讲就是在数据库里面额外增加一个outbox表用于存储需要发送的event,把直接发送event的步骤换成先把event存储到数据库outbox表;另外,程序启动一个scheduler job不断去抓取outbox表里面的记录,发送给Kafka,最后删除发送成功的记录。如下:

主逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
begin transactionsave(order) to dbsave(order_created_event) to db outboxcommit/rollback

scheduler job:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
begin transactionget(order_created_event) from db outboxsend(order_created_event) to kafkadelete(order_created_event) to db outboxcommit/rollback

基于这样的实现,存order和event就可以通过数据库的transaction来保障,这样即使是Kafka不available,也不影响用户的行为,只是说后续处理可能会有一些延迟而已;此外,如果scheduler job删除event失败,最坏的行为也就是重新再发一次这个event,即是重发消息的问题,这就需要消费端实现幂等性处理。

3

发件箱模式主要有两种实现方式:

  • Transaction log tailing
  • Polling publisher

Transaction log tailing,又称CDC(change data capture),就是依赖于数据库的transaction log,一般数据库在成功执行完一条语句之后就会记录一条log,那么这个方式就是不断地增量抓取数据库log,然后发送到消息系统。目前,已经有一些支持这种方式的open source可以直接使用,比如:Debezium。另外,采用这种方式,可能需要数据库装相关的插件以支持CDC。

Polling publisher,其实就是自己实现一个轮询的sheduler job,不断抓取outbox表里面的event,然后发送到消息系统,最后删除event。在实现层面,可能需要注意下面几点:

  1. 一般来说,为了保证event的顺序,在抓取event的时候,需要把event按时间排序。
  2. 如果是分布式的环境,为了保证scheduler job同时只在一个instance上run,需要实现分布式锁;或者借助于成熟的scheduling的library,比如:quartz。

相关阅读

References

  • https://microservices.io/patterns/data/transactional-outbox.html
  • https://mp.weixin.qq.com/s/PMIWr8j3cN-K0FbS6qGW0A
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 天马行空布鲁斯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
空谈分布式系统设计之幂等性
在之前的文章,有多次提到转账系统这个案例,由于这个案例太典型了,很多大学教授数据库事务的时候就是用的这个案例。
Bruce Li
2020/10/14
9600
如何使用发件箱模式实现微服务的 Saga 编排
作者 | Gunnar Morling 译者 | 张卫滨 核心要点 Saga 能够实现长时间运行的、分布式的业务事务,这样的事务会跨多个微服务执行一组操作,实现一致的全有或全无的语义。 为了实现解耦,微服务之间的通信最好按照异步的方式来进行,比如借助 Apache Kafka 使用分布式的提交日志。 发件箱模式为服务作者提供了一种解决方案,能够让他们在本地数据库执行写入,同时通过 Apache Kafka 发送消息,避免依赖不安全的“双重写入(dual writes)”。 Debezium 是一个分布式
深度学习与Python
2023/04/01
7110
如何使用发件箱模式实现微服务的 Saga 编排
你知道微服务架构中的“发件箱模式”吗
微服务架构如今非常的流行,这个架构下可能经常会遇到“双写”的场景。双写是指您的应用程序需要在两个不同的系统中更改数据的情况,比如它需要将数据存储在数据库中并向消息队列发送事件。您需要保证这两个操作都会成功。如果两个操作之一失败,您的系统可能会变得不一致。那针对这样的情况有什么好的方法或者设计保证呢?本文就和大家分享一个“发件箱模式”, 可以很好的避免此类问题。
程序猿川子
2023/01/09
3040
消息架构的设计难题以及应对之道
在微服务开发中我们经常会引入消息中间件实现业务解耦,执行异步操作, 现在让我们来看看使用消息中间件的好处和弊端。
JAVA日知录
2020/11/11
7620
消息架构的设计难题以及应对之道
使 API 具有弹性:使用发件箱模式提高 .NET 微服务的可靠性
在微服务的世界里,我们都遇到过事情未按计划进行的情况。想象一下这样的场景:你有一个微服务,它会将新订单保存到数据库中,然后发布一条消息来通知其他服务。一切原本都很顺利……直到消息发布失败,导致你系统的部分环节失去同步。好消息是,这正是“发件箱模式”(Outbox Pattern)大显身手的地方。
郑子铭
2025/01/20
1910
使 API 具有弹性:使用发件箱模式提高 .NET 微服务的可靠性
分布式事务 | 使用 dotnetcore/CAP 的本地消息表模式
本地消息表模式,其作为柔性事务的一种,核心是将一个分布式事务拆分为多个本地事务,事务之间通过事件消息衔接,事件消息和上个事务共用一个本地事务存储到本地消息表,再通过定时任务轮询本地消息表进行消息投递,下游业务订阅消息进行消费,本质上是依靠消息的重试机制达到最终一致性。其示意图如下所示,主要分为以下三步:
圣杰
2023/02/10
8000
分布式事务 | 使用 dotnetcore/CAP 的本地消息表模式
分布式事务解决方案及java实例
用户7353950
2024/04/03
3380
分布式事务解决方案及java实例
图解7种分布式事务模型(一文带你掌握分布式事务)
Hello 我是方才,10人研发leader、4年团队管理&架构经验。文末,方才送你一份25年最新的架构师备考资料,记得领取哟!
方才编程_公众号同名
2025/04/02
3860
图解7种分布式事务模型(一文带你掌握分布式事务)
从一笔金币充值去思考分布式事务
考虑支付重构的时候,自然想到原本属于一个本地事务中的处理,现在要跨应用了要怎么处理。拿充值订单举个栗子吧,假设:原本订单模块和账户模块是放在一起的,现在需要做服务拆分,拆分成订单服务,账户服务。原本收到充值回调后,可以将修改订单状态和增加金币放在一个mysql事务中完成的,但是呢,因为服务拆分了,就面临着需要协调2个服务才能完成这个事务
java思维导图
2018/12/13
6640
从一笔金币充值去思考分布式事务
看了 5 种分布式事务方案,我司最终选择了 Seata,真香!
好长时间没发文了,最近着实是有点忙,当爹的第 43 天,身心疲惫。这又赶上年底,公司冲 KPI 强制技术部加班到十点,晚上孩子隔两三个小时一醒,基本没睡囫囵觉的机会,天天处于迷糊的状态,孩子还时不时起一些奇奇怪怪的疹子,总让人担惊受怕的。
程序员小富
2020/11/27
6380
看了 5 种分布式事务方案,我司最终选择了 Seata,真香!
微服务下分布式事务模式的详细对比
作为 Red Hat 咨询架构师,我有幸参与了大量客户项目。虽然每个客户都面临自己特有的挑战,但是我发现其中有一些共同点。大多数项目都想知道如何协调对多个记录系统的写入。要回答这个问题,一般会涉及长篇累牍的解释,包括双重写入(dual write)、分布式事务、现代化的替代方案以及每种方式可能出现的故障情况和缺点。这样做通常会让客户意识到,将单体应用拆分为微服务架构是一个漫长和复杂的过程,而且通常都需要权衡。
深度学习与Python
2021/12/13
7970
微服务下分布式事务模式的详细对比
比较微服务中的分布式事务模式
译自:Distributed transaction patterns for microservices compared
charlieroro
2021/12/01
2.6K0
比较微服务中的分布式事务模式
SpringCloud Alibaba微服务实战三十二 - 集成RocketMQ实现分布式事务
分布式事务是在微服务开发中经常会遇到的一个问题,之前的文章中我们已经实现了利用Seata来实现强一致性事务,其实还有一种广为人知的方案就是利用消息队列来实现分布式事务,保证数据的最终一致性,也就是我们常说的柔性事务。
JAVA日知录
2021/06/16
2.2K0
SpringCloud Alibaba微服务实战三十二 - 集成RocketMQ实现分布式事务
Java分布式锁、分布式ID和分布式事务的实现方案
在分布式系统中,分布式锁、分布式ID和分布式事务是常用的组件,用于解决并发控制、唯一标识和数据一致性的问题。本文将介绍Java中常用的分布式锁、分布式ID和分布式事务的实现方案,并通过具体的示例代码演示它们的用法和应用场景。
王也518
2023/12/22
7390
事件驱动微服务中的分布式事务模式
如今,软件服务可以由多个微服务组成,共同维护系统的 "状态"。在分布式软件设计中,最常见的挑战之一就是保持一致性。当构成整体服务的不同服务与系统状态不一致时,不一致的系统会引发各种问题。
JavaEdge
2025/01/11
1970
分布式事务的 N 种实现
在微服务架构中,随着服务的逐步拆分,数据库私有已经成为共识,这也导致所面临的分布式事务问题成为微服务落地过程中一个非常难以逾越的障碍,但是目前尚没有一个完整通用的解决方案。
架构之家
2022/07/12
3290
分布式事务的 N 种实现
分布式事务两阶段提交和三阶段提交有什么区别?
在分布式事务中,通常使用两阶段协议或三阶段协议来保障分布式事务的正常运行,它也是 X/Open 公司定义的一套分布式事务标准。
磊哥
2023/07/27
7250
分布式事务两阶段提交和三阶段提交有什么区别?
Seata--分布式事务
事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销。简单地说,事务提供一种“要么什么都不做,要么做全套”机制。
IT小马哥
2021/09/03
3810
Seata--分布式事务
如何使用消息队列的事务消息
“发消息”过程,往往是为通知另外一个系统更新数据,MQ的“事务”,主要解决消息生产者和消息消费者的数据一致性问题。
JavaEdge
2021/02/22
2.3K0
如何使用消息队列的事务消息
MQ的数据一致性,如何保证?
上个月,我们有个电商系统出了个灵异事件:用户支付成功了,但订单状态死活不改成“已发货”。
苏三说技术
2025/03/28
2220
MQ的数据一致性,如何保证?
推荐阅读
相关推荐
空谈分布式系统设计之幂等性
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验