比 RocketMQ 更好的事务消息实现是什么?

先抛出的一个问题:一个事务涉及 mysql 和 mq,到底哪个写入成功重要?

假如线上 mq 集群网络故障,导致发消息失败,即使 mysql 还是活着的,但是无法进行事务。

所以其实这个问题问的是:mysql 和 mq 之间的写入顺序。

在 RocketMQ 中,事务消息的实现方案是先发半消息(半消息对消费者不可见),待半消息发送成功之后,才能执行本地事务,等本地事务执行成功之后,再向 Broker 发送请求将半消息转成正常消息,这样消费者就可以消费此消息。

这种顺序等于先得成功写入 mq,然后再写入数据库,这样的模式会出现一个问题:即 mq 集群挂了,事务就无法继续进行了,等于整个应用无法正常执行了。

看一下我之前画的 RocketMQ 事务消息流程图:

所以,先写 mq 后写 mysql 就会发生 mysql 还好好的,但是 mq 挂了事务就无法正常执行的情况。

qmq 事务消息

QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。目前在公司内部日常消息qps在60W左右,生产上承载将近4W+消息topic,消息的端到端延迟可以控制在10ms以内。

在说 qmq 的事务消息之前,先来说下本地消息表这个分布式事务实现方式。

本地消息就是利用了关系型数据库的事务能力,会在数据库中存放一张本地事务消息表,在进行本地事务操作中加入了本地消息表的插入,即将业务的执行和将消息放入到消息表中的操作放在同一个事务中提交。

这样本地事务执行成功的话,消息肯定也插入成功,然后再调用其他服务,如果其他服务调用成功就修改这条本地消息的状态。

如果失败也不要紧,会有一个后台线程扫描,发现这些状态的消息,会一直调用相应的服务,一般会设置重试的次数,如果一直不行则特殊记录,待人工介入处理。

可以看到,本地事务消息表还是很简单的,也是一种最大努力通知的思想。

在理解本地消息表之后,我们再来看一下 qmq 的事务消息是如何设计的。首先,想要用 qmq 的事务消息,需要在数据库中建一张表,就是如下这样的表:

是不是有本地消息表那味儿了?

没错核心思想就是本地消息表!利用关系型数据库的事务能力,将业务的写入和消息表的写入融在一个事务中,这样业务成功则消息表肯定写入成功。

然后在事务提交之后,立刻发送事务消息,如果发送成功,则删除本地消息表中的记录。如果消息发送失败,也就是比如 mq 集群挂了,并不会影响事务的执行,业务的执行和事务消息的插入都已经成功了,那此时待消息已经安安静静的在消息库里等着,后台能会有一个补偿任务,会将这些消息捞出来重新发送,直到发送成功。

它的顺序就属于先写数据库,再发mq,即使mq 集群挂了,也不会影响事务的进行,不会导致应用无法正常执行了。

这里可能有人会问,那如果 mysql 挂了呢?

我只能说数据库都挂了,那就都没了,别想啥别的了。

QMQ 事务消息更优的原因

至此,想必你已经清楚RocketMQ 和 QMQ 事务消息的区别,我们再来盘下 QMQ 事务消息更优的原因。

RocketMQ 只支持单事务消息,也就是无法在一个事务内发送多种事务消息。而 QMQ 可以在一次事务中发多个消息.。

然后 RocketMQ 事务消息的实现还需要提供一个反查机制,因为RocketMQ 事务消息的提交是 oneway的发送方式,有可能 Broker 没有接收到事务提交的消息,所以 Broker 会定时去生产者那边查看事务是否已经执行完成,因此生产者需要保存本地事务执行结果,简单的就是用一个 map 保存,让 Broker 可以通过消息的事务 id 查找到事务执行的结果。

如果还要考虑发送事务消息的生产者挂了,那么 Broker 会找同个生产组的其他生产者来查询事务结果,所以这个存储还得提出来放到第三方,而不是本地内存保存。

因此,RocketMQ 得多维护一个本地事务执行结果,是稍微有点麻烦的。

当然,QMQ 还得建表呢,不过按照 QMQ 说的:如果公司方便的话,可以直接合并进DBA的初始化数据库的自动化流程中,这样就透明了。

展开阅读全文

页面更新:2024-04-19

标签:生产组   事务   消息   生产者   集群   顺序   后台   场景   数据库   业务

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top