分布式事务消息 如何确保远程事务能执行成功?先执行本地事务,还是先发送异步消息? 柔性事务 单点故障问题 传统的分布式事务通过 XA 模型实现
如何通过事务消息保障抢购业务的分布式一致性? https://mp.weixin.qq.com/s/XA-CsdBxgbXdsIjKOdyoGQ
分布式事务的实现方式
传统分布式事务
传统的分布式事务通过 XA 模型实现,通过一个事务协调者,站在全局的角度将多个子事务合并成一个分布式事务。XA 模型之所以能在分布式事务领域得到广泛使用,是因为其具有如下两个方面的优势:
-
提供了强一致性保证,在业务执行的任何时间点都能确保事务一致性。
-
使用简单。常见的关系型数据库都提供了对XA协议的支持,通过引入事务协调器,业务代码跟使用单机事务相比基本上没有差别。
但是在互联网领域,XA 模型的分布式事务实现存在很多局限性,在抢购业务这样的高并发大流量场景中更是被完全弃用。我们拿 XA 分布式协议中最普遍的两阶式提交方案,来说明为什么 XA 模型并不适合互联网场景。
1、性能问题。在两段式提交的执行过程中,所有参与节点都是事务阻塞型的,需要长时间锁定资源。这会导致系统整体的并发吞吐量变低,在抢购业务中是不可接受的。2、单点故障问题。事务协调者在链路中有着至关重要的作用,一旦协调者发生故障,参与者会一直阻塞下去,整个系统将无法工作,因此需要投入巨大的精力来保障事务协调者的高可用性。3、数据不一致问题。在阶段二中,如果协调者向参与者发送 commit 请求之后,发生了网络异常,会导致只有一部分参与者接收到了 commit 请求,没有接收到 commit 请求的参与者最终会执行回滚操作,从而造成数据不一致现象。在抢购业务中,这样的数据不一致有可能会对企业或消费者造成巨大的经济损失。
因此 XA 模型是一个理想化的分布式事务模型,并没有考虑到高并发、网络故障等实际因素,即便是在两阶段提交的基础上,诞生了三阶段提交这样的实现方式,也没有办法从根本上解决性能和数据不一致的问题。
柔性事务
针对传统分布式事务方案在互联网领域的局限性,业界提出了 CAP 理论以及 BASE 理论,在此基础上诞生了在大型互联网应用中广泛使用的柔性事务。柔性事务的核心思想是放弃传统分布式事务中对于严格一致性的要求,允许在事务执行过程中存在数据不一致的中间状态,在业务上需要容忍中间状态的存在。柔性事务会提供完善的机制,保证在一段时间的中间状态后,系统能走向最终一致状态。
遵循 BASE 理论的柔性事务放弃了隔离性,减小了事务中锁的粒度,使得应用能够更好的利用数据库的并发性能,实现吞吐量的线性扩展。异步执行方式可以更好地适应分布式环境,在网络抖动、节点故障的情况下能够尽量保障服务的可用性。因此在高并发、大流量的抢购业务中,柔性事务是最佳的选择。
传统分布式事务 | 柔性事务 | |
业务改造 | 无 | 有 |
一致性 | 强一致性 | 最终一致 |
回滚 | 支持 | 实现回退接口 |
隔离性 | 支持 | 放弃隔离性或实现资源锁定接口 |
并发性能 | 低 | 高 |
适合场景 | 低并发、短事务 | 高并发、长事务 |
柔性事务有多种实现方式,包括TCC、Saga、事务消息、最大努力通知等,本文将重点介绍通过事务消息实现柔性事务。
事务消息原理分析
抢购业务场景拆解
我们结合抢购业务的真实场景,分析如何通过事务消息实现分布式一致性。在抢购业务中,有两个非常关键的阶段,需要引入分布式事务机制,分别是订单创建阶段和付款成功阶段。
从字面含义来看,抢购业务就隐含了一个重要的前提:库存是有限的。因此在订单创建的时候,需要预先检查库存情况,并相对应的库存进行锁定,以防止商品超卖。如果库存锁定操作失败,代表库存不足,必须确保订单不能被成功创建。在锁定库存后,如果因为某种异常情况导致订单创建失败,必须及时将之前锁定的库存进行释放操作,以便让其他用户可以重新争夺对应的商品。
如果抢购系统实现了购物车机制,在订单创建的同时,则需要从购买车中将相应的条目删除。
基于微服务架构的业务拆分,订单创建阶段的 3 个行为很有可能通过 3 个不同的微服务应用完成,因此需要通过分布式事务来保证数据一致性。
订单创建完成后,会等待用户付款,一旦付款成功,就会触发付款成功阶段的执行逻辑。这个阶段同样是通过分布式事务来完成,包含修改订单状态、扣减库存、通知发货、增加积分这4个子事务,它们要么全部不执行,要么全部执行成功。
当然,在真实的抢购业务中,情况有可能会更加的复杂,本文列出的只是其中最具代表性的几类业务行为。
引入消息异步通知机制
传统的分布式事务存在一个很大的弊端是参与节点都是事务阻塞型的,需要长时间锁定资源。以锁定库存 ->创建订单这个流程为例,借助于 Redis 等缓存系统,单纯锁定库存的操作只需要花费毫秒级的时间,可以承载非常高的并发量。但如果把创建订单的操作也考虑进来,加上不同微服务应用之间相互通讯的时候,整体耗时有可能超过1秒,导致性能急剧下降。
假设存在一种异步消息机制,让分布式事务的第一个参与方在执行完本地事务后,通过触发一笔消息通知事务的其他参与方完成后续的操作,就能将大事务拆解为多个本地子事务来分开执行。在这种模式下,事务的多个参与方之间之间并不需要彼此阻塞和等待,就能极大程度地提升并发吞吐能力。
对于库存中心而言,在高并发场景下,只需要不断的执行锁定库存记录操作,并不断通过异步消息通知订单中心创建订单,只要异步消息机制能确保消息一定送达,并得到正确处理,就能够实现分布式最终一致性。
先执行本地事务,还是先发送异步消息?
在这个模型中,异步消息的发送交给了分布式事务的第一个参与方来完成,这个参与方就拥有了两个职责:执行本地事务和发送异步消息。到底应该先执行本地事务,还是先发异步消息呢?
第一种方案是先发送异步消息,再执行本地事务。这样做肯定是不行的,如果本地事务没有执行成功,异步消息已经发出去了,其他事务参与方收到消息后会执行对应的远程事务,造成数据不一致。
第二种方案是先执行本地事务,再发送异步消息。这样做能够解决本地事务执行失败导致的数据不一致问题,因为只有在本地事务执行成功的情况下,才会发送异步消息。但如果事务的参与方在执行本地事务成功后,自己宕机了,就再有没有机会发送异步消息了,因此这样做同样会造成数据不一致的问题。记住:在真实场景中,任何一个应用节点都不是 100% 可靠的,都存在宕机的可能性。
一个可行的方案是引入可以处理事务消息的消息队列集群,用于异步消息的中转。一个事务消息包含两种形态:
1、首先,事务的参与方发送一笔半事务消息到消息队列,表示自己即将执行本地事务,消息队列集群在收到这个半事务消息后,不会马上进行投递,而是进行暂存。
2、在执行完本地事务后,事务的参考方再发送一笔确认消息到消息队列集群,告知本地事务的执行状态。如果本地事务执行成功,消息队列集群会将之前收到的半事务消息进行投递;如果本地事务执行失败,消息队列集群直接删除之前收到的半事务消息,这样远程事务就不会被执行,从而保证了最终一致性。
同样,如果事务参与方在执行完本地事务后宕机了怎么办呢?这就需要消息队列集群具备回查机制:如果收到半事务消息后,在特定时间内没有再收到确认消息,会反过来请求事务参与方查询本地事务的执行状态,并给予反馈。这样,即便错过了确认消息,消息队列集群也有能力了解到本地事务的执行状态,从而决定是否将消息进行投递。
在一个微服务应用中,会存在多个对等的应用实例,这也就代表着即便一个事务参与方的实例在执行完本地事务后宕机了,消息队列集群依然可以通过这个实例的兄弟实例了解到本地事务执行的最终状态。
如何确保远程事务能执行成功?
如果一切本地事务的执行,以及异步消息的投递都一切顺利的话,接下来还会存在另外两种数据不一致的可能性:
-
消息队列集群在将异步消息投递到远程事务参与方的时候,由于网络不稳定,消息没能投递成功。
-
消息投递成功了,但远程事务参与方还没来得及执行远程事务,就宕机了。
这两种情况都会导致远程事务执行失败,所以需要建立一种消息重试机制,让远程事务参与方在完成任务后(实际上对远程事务参与方而言,这个任务是它要执行的本地任务),给予消息队列集群一个反馈,告知异步消息已经得到了正确的处理。否则,消息队列会在一定时间后,周期性的重复投递消息,直到它收到了来自远程事务参与方的反馈,以确保远程事务一定能执行成功。
和事务回查机制类似,远程事务参与方也有多个对等的微服务实例,即便某个实例在没来得及执行远程事务的时候宕机,消息队列也可以将任务交给这个实例的兄弟实例来完成。
完整流程
事务消息 | RocketMQ https://rocketmq.apache.org/zh/docs/featureBehavior/04transactionmessage
事务消息
事务消息为 Apache RocketMQ 中的高级特性消息,本文为您介绍事务消息的应用场景、功能原理、使用限制、使用方法和使用建议。
应用场景
分布式事务的诉求
分布式系统调用的特点为一个核心业务逻辑的执行,同时需要调用多个下游业务进行处理。因此,如何保证核心业务和多个下游业务的执行结果完全一致,是分布式事务需要解决的主要问题。
以电商交易场景为例,用户支付订单这一核心操作的同时会涉及到下游物流发货、积分变更、购物车状态清空等多个子系统的变更。当前业务的处理分支包括:
-
主分支订单系统状态更新:由未支付变更为支付成功。
-
物流系统状态新增:新增待发货物流记录,创建订单物流记录。
-
积分系统状态变更:变更用户积分,更新用户积分表。
-
购物车系统状态变更:清空购物车,更新用户购物车记录。
传统XA事务方案:性能不足
为了保证上述四个分支的执行结果一致性,典型方案是基于XA协议的分布式事务系统来实现。将四个调用分支封装成包含四个独立事务分支的大事务。基于XA分布式事务的方案可以满足业务处理结果的正确性,但最大的缺点是多分支环境下资源锁定范围大,并发度低,随着下游分支的增加,系统性能会越来越差。
基于普通消息方案:一致性保障困难
将上述基于XA事务的方案进行简化,将订单系统变更作为本地事务,剩下的系统变更作为普通消息的下游来执行,事务分支简化成普通消息+订单表事务,充分利用消息异步化的能力缩短链路,提高并发度。
该方案中消息下游分支和订单系统变更的主分支很容易出现不一致的现象,例如:
-
消息发送成功,订单没有执行成功,需要回滚整个事务。
-
订单执行成功,消息没有发送成功,需要额外补偿才能发现不一致。
-
消息发送超时未知,此时无法判断需要回滚订单还是提交订单变更。
基于Apache RocketMQ分布式事务消息:支持最终一致性
上述普通消息方案中,普通消息和订单事务无法保证一致的原因,本质上是由于普通消息无法像单机数据库事务一样,具备提交、回滚和统一协调的能力。
而基于Apache RocketMQ实现的分布式事务消息功能,在普通消息基础上,支持二阶段的提交能力。将二阶段提交和本地事务绑定,实现全局提交结果的一致性。
Apache RocketMQ事务消息的方案,具备高性能、可扩展、业务开发简单的优势。具体事务消息的原理和流程,请参见下文的功能原理。
功能原理
什么是事务消息
事务消息是 Apache RocketMQ 提供的一种高级消息类型,支持在分布式场景下保障消息生产和本地事务的最终一致性。
事务消息处理流程
事务消息交互流程如下图所示。
-
生产者将消息发送至Apache RocketMQ服务端。
-
Apache RocketMQ服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息。
-
生产者开始执行本地事务逻辑。
-
生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:
-
二次确认结果为Commit:服务端将半事务消息标记为可投递,并投递给消费者。
-
二次确认结果为Rollback:服务端将回滚事务,不会将半事务消息投递给消费者。
-
-
在断网或者是生产者应用重启的特殊情况下,若服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。 说明 服务端回查的间隔时间和最大回查次数,请参见参数限制。
-
生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
-
生产者根据检查到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。
事务消息生命周期
-
初始化:半事务消息被生产者构建并完成初始化,待发送到服务端的状态。
-
事务待提交:半事务消息被发送到服务端,和普通消息不同,并不会直接被服务端持久化,而是会被单独存储到事务存储系统中,等待第二阶段本地事务返回执行结果后再提交。此时消息对下游消费者不可见。
-
消息回滚:第二阶段如果事务执行结果明确为回滚,服务端会将半事务消息回滚,该事务消息流程终止。
-
提交待消费:第二阶段如果事务执行结果明确为提交,服务端会将半事务消息重新存储到普通存储系统中,此时消息对下游消费者可见,等待被消费者获取并消费。
-
消费中:消息被消费者获取,并按照消费者本地的业务逻辑进行处理的过程。 此时服务端会等待消费者完成消费并提交消费结果,如果一定时间后没有收到消费者的响应,Apache RocketMQ会对消息进行重试处理。具体信息,请参见消费重试。
-
消费提交:消费者完成消费处理,并向服务端提交消费结果,服务端标记当前消息已经被处理(包括消费成功和失败)。 Apache RocketMQ默认支持保留所有消息,此时消息数据并不会立即被删除,只是逻辑标记已消费。消息在保存时间到期或存储空间不足被删除前,消费者仍然可以回溯消息重新消费。
-
消息删除:Apache RocketMQ按照消息保存机制滚动清理最早的消息数据,将消息从物理文件中删除。更多信息,请参见消息存储和清理机制。
使用限制
消息类型一致性
事务消息仅支持在 MessageType 为 Transaction 的主题内使用,即事务消息只能发送至类型为事务消息的主题中,发送的消息的类型必须和主题的类型一致。
消费事务性
Apache RocketMQ 事务消息保证本地主分支事务和下游消息发送事务的一致性,但不保证消息消费结果和上游事务的一致性。因此需要下游业务分支自行保证消息正确处理,建议消费端做好消费重试,如果有短暂失败可以利用重试机制保证最终处理成功。
中间状态可见性
Apache RocketMQ 事务消息为最终一致性,即在消息提交到下游消费端处理完成之前,下游分支和上游事务之间的状态会不一致。因此,事务消息仅适合接受异步执行的事务场景。
事务超时机制
Apache RocketMQ 事务消息的命周期存在超时机制,即半事务消息被生产者发送服务端后,如果在指定时间内服务端无法确认提交或者回滚状态,则消息默认会被回滚。事务超时时间,请参见参数限制。
事务消息 https://help.aliyun.com/document_detail/43348.html
消息队列RocketMQ版提供的分布式事务消息适用于所有对数据最终一致性有强需求的场景。本文介绍消息队列RocketMQ版事务消息的概念、优势、典型场景、交互流程、使用规则以及示例代码。
概念介绍
- 事务消息:消息队列RocketMQ版提供类似XA或Open XA的分布式事务功能,通过消息队列RocketMQ版事务消息能达到分布式事务的最终一致。
- 半事务消息:暂不能投递的消息,生产者已经成功地将消息发送到了消息队列RocketMQ版服务端,但是消息队列RocketMQ版服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半事务消息。
- 消息回查:由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,消息队列RocketMQ版服务端通过扫描发现某条消息长期处于“半事务消息”时,需要主动向消息生产者询问该消息的最终状态(Commit或是Rollback),该询问过程即消息回查。
分布式事务消息的优势
消息队列RocketMQ版分布式事务消息不仅可以实现应用之间的解耦,又能保证数据的最终一致性。同时,传统的大事务可以被拆分为小事务,不仅能提升效率,还不会因为某一个关联应用的不可用导致整体回滚,从而最大限度保证核心系统的可用性。在极端情况下,如果关联的某一个应用始终无法处理成功,也只需对当前应用进行补偿或数据订正处理,而无需对整体业务进行回滚。
典型场景
在淘宝购物车下单时,涉及到购物车系统和交易系统,这两个系统之间的数据最终一致性可以通过分布式事务消息的异步处理实现。在这种场景下,交易系统是最为核心的系统,需要最大限度地保证下单成功。而购物车系统只需要订阅消息队列RocketMQ版的交易订单消息,做相应的业务处理,即可保证最终的数据一致性。
交互流程
事务消息发送步骤如下:
- 生产者将半事务消息发送至消息队列RocketMQ版服务端。
- 消息队列RocketMQ版服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息为半事务消息。
- 生产者开始执行本地事务逻辑。
- 生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:
- 二次确认结果为Commit:服务端将半事务消息标记为可投递,并投递给消费者。
- 二次确认结果为Rollback:服务端将回滚事务,不会将半事务消息投递给消费者。
- 在断网或者是生产者应用重启的特殊情况下,若服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。
事务消息回查步骤如下:
- 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
- 生产者根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。
使用规则
生产消息规则
- 事务消息发送完成本地事务后,可在
execute
方法中返回以下三种状态:TransactionStatus.CommitTransaction
:提交事务,允许消费者消费该消息。TransactionStatus.RollbackTransaction
:回滚事务,消息将被丢弃不允许消费。TransactionStatus.Unknow
:暂时无法判断状态,等待固定时间以后消息队列RocketMQ版服务端根据回查规则向生产者进行消息回查。
- 通过
ONSFactory.createTransactionProducer
创建事务消息的Producer时必须指定LocalTransactionChecker
的实现类,处理异常情况下事务消息的回查。 - 回查规则:本地事务执行完成后,若消息队列RocketMQ版服务端收到的本地事务返回状态为
TransactionStatus.Unknow
,或生产者应用退出导致本地事务未提交任何状态。则消息队列RocketMQ版服务端会向消息生产者发起事务回查,第一次回查后仍未获取到事务状态,则之后每隔一段时间会再次回查。- 回查间隔时间:系统默认每隔30秒发起一次定时任务,对未提交的半事务消息进行回查,共持续12小时。
- 第一次消息回查最快时间:该参数支持自定义设置。若指定消息未达到设置的最快回查时间前,系统默认每隔30秒一次的回查任务不会检查该消息。
以Java为例,以下设置表示:第一次回查的最快时间为60秒。
Message message = new Message(); message.putUserProperties(PropertyKeyConst.CheckImmunityTimeInSeconds,"60");
说明因为系统默认的回查间隔,第一次消息回查的实际时间会向后有0秒~30秒的浮动。
例如:指定消息的第一次消息最快回查时间设置为60秒,系统在第58秒时达到定时的回查时间,但设置的60秒未到,所以该消息不在本次回查范围内。等待间隔30秒后,下一次的系统回查时间在第88秒,该消息才符合条件进行第一次回查,距设置的最快回查时间延后了28秒。
消费消息规则
- 事务消息的Group ID不能与其他类型消息的Group ID共用。与其他类型的消息不同,事务消息有回查机制,回查时消息队列RocketMQ版服务端会根据Group ID去查询生产者客户端。
事务消息
事务消息为 Apache RocketMQ 中的高级特性消息,本文为您介绍事务消息的应用场景、功能原理、使用限制、使用方法和使用建议。
应用场景
分布式事务的诉求
分布式系统调用的特点为一个核心业务逻辑的执行,同时需要调用多个下游业务进行处理。因此,如何保证核心业务和多个下游业务的执行结果完全一致,是分布式事务需要解决的主要问题。
以电商交易场景为例,用户支付订单这一核心操作的同时会涉及到下游物流发货、积分变更、购物车状态清空等多个子系统的变更。当前业务的处理分支包括:
-
主分支订单系统状态更新:由未支付变更为支付成功。
-
物流系统状态新增:新增待发货物流记录,创建订单物流记录。
-
积分系统状态变更:变更用户积分,更新用户积分表。
-
购物车系统状态变更:清空购物车,更新用户购物车记录。
传统XA事务方案:性能不足
为了保证上述四个分支的执行结果一致性,典型方案是基于XA协议的分布式事务系统来实现。将四个调用分支封装成包含四个独立事务分支的大事务。基于XA分布式事务的方案可以满足业务处理结果的正确性,但最大的缺点是多分支环境下资源锁定范围大,并发度低,随着下游分支的增加,系统性能会越来越差。
基于普通消息方案:一致性保障困难
将上述基于XA事务的方案进行简化,将订单系统变更作为本地事务,剩下的系统变更作为普通消息的下游来执行,事务分支简化成普通消息+订单表事务,充分利用消息异步化的能力缩短链路,提高并发度。
该方案中消息下游分支和订单系统变更的主分支很容易出现不一致的现象,例如:
-
消息发送成功,订单没有执行成功,需要回滚整个事务。
-
订单执行成功,消息没有发送成功,需要额外补偿才能发现不一致。
-
消息发送超时未知,此时无法判断需要回滚订单还是提交订单变更。
基于Apache RocketMQ分布式事务消息:支持最终一致性
上述普通消息方案中,普通消息和订单事务无法保证一致的原因,本质上是由于普通消息无法像单机数据库事务一样,具备提交、回滚和统一协调的能力。
而基于Apache RocketMQ实现的分布式事务消息功能,在普通消息基础上,支持二阶段的提交能力。将二阶段提交和本地事务绑定,实现全局提交结果的一致性。
Apache RocketMQ事务消息的方案,具备高性能、可扩展、业务开发简单的优势。具体事务消息的原理和流程,请参见下文的功能原理。
功能原理
什么是事务消息
事务消息是 Apache RocketMQ 提供的一种高级消息类型,支持在分布式场景下保障消息生产和本地事务的最终一致性。
事务消息处理流程
事务消息交互流程如下图所示。
-
生产者将消息发送至Apache RocketMQ服务端。
-
Apache RocketMQ服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息。
-
生产者开始执行本地事务逻辑。
-
生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:
-
二次确认结果为Commit:服务端将半事务消息标记为可投递,并投递给消费者。
-
二次确认结果为Rollback:服务端将回滚事务,不会将半事务消息投递给消费者。
-
-
在断网或者是生产者应用重启的特殊情况下,若服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。 说明 服务端回查的间隔时间和最大回查次数,请参见参数限制。
-
生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
-
生产者根据检查到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。
事务消息生命周期
-
初始化:半事务消息被生产者构建并完成初始化,待发送到服务端的状态。
-
事务待提交:半事务消息被发送到服务端,和普通消息不同,并不会直接被服务端持久化,而是会被单独存储到事务存储系统中,等待第二阶段本地事务返回执行结果后再提交。此时消息对下游消费者不可见。
-
消息回滚:第二阶段如果事务执行结果明确为回滚,服务端会将半事务消息回滚,该事务消息流程终止。
-
提交待消费:第二阶段如果事务执行结果明确为提交,服务端会将半事务消息重新存储到普通存储系统中,此时消息对下游消费者可见,等待被消费者获取并消费。
-
消费中:消息被消费者获取,并按照消费者本地的业务逻辑进行处理的过程。 此时服务端会等待消费者完成消费并提交消费结果,如果一定时间后没有收到消费者的响应,Apache RocketMQ会对消息进行重试处理。具体信息,请参见消费重试。
-
消费提交:消费者完成消费处理,并向服务端提交消费结果,服务端标记当前消息已经被处理(包括消费成功和失败)。 Apache RocketMQ默认支持保留所有消息,此时消息数据并不会立即被删除,只是逻辑标记已消费。消息在保存时间到期或存储空间不足被删除前,消费者仍然可以回溯消息重新消费。
-
消息删除:Apache RocketMQ按照消息保存机制滚动清理最早的消息数据,将消息从物理文件中删除。更多信息,请参见消息存储和清理机制。
使用限制
消息类型一致性
事务消息仅支持在 MessageType 为 Transaction 的主题内使用,即事务消息只能发送至类型为事务消息的主题中,发送的消息的类型必须和主题的类型一致。
消费事务性
Apache RocketMQ 事务消息保证本地主分支事务和下游消息发送事务的一致性,但不保证消息消费结果和上游事务的一致性。因此需要下游业务分支自行保证消息正确处理,建议消费端做好消费重试,如果有短暂失败可以利用重试机制保证最终处理成功。
中间状态可见性
Apache RocketMQ 事务消息为最终一致性,即在消息提交到下游消费端处理完成之前,下游分支和上游事务之间的状态会不一致。因此,事务消息仅适合接受异步执行的事务场景。
事务超时机制
Apache RocketMQ 事务消息的命周期存在超时机制,即半事务消息被生产者发送服务端后,如果在指定时间内服务端无法确认提交或者回滚状态,则消息默认会被回滚。事务超时时间,请参见参数限制。
使用示例
创建主题
Apache RocketMQ 5.0版本下创建主题操作,推荐使用mqadmin工具,需要注意的是,对于消息类型需要通过属性参数添加。示例如下:
sh mqadmin updateTopic -n <nameserver_address> -t <topic_name> -c <cluster_name> -a +message.type=Transaction
发送消息
事务消息相比普通消息发送时需要修改以下几点:
-
发送事务消息前,需要开启事务并关联本地的事务执行。
-
为保证事务一致性,在构建生产者时,必须设置事务检查器和预绑定事务消息发送的主题列表,客户端内置的事务检查器会对绑定的事务主题做异常状态恢复。
以Java语言