当前位置: 首页 > 后端技术 > Java

分布式事务(6)可靠消息最终一致性

时间:2023-04-01 14:14:01 Java

消息发送一致性:指产生消息的业务动作与消息发送的一致性。也就是说,如果业务运行成功,则本次业务运行产生的消息必须成功投递(一般是发送给kafka、rocketmq、rabbitmq等消息中间件),否则消息会丢失。可靠消息的最终一致性发送消息的不可靠性既然提到了可靠消息的最终一致性,那么说明现有的消息发送逻辑是不可靠的。我们使用以下情况来证明消息的不可靠性。先进行数据库操作,再发送消息:publicvoidtest1(){//1数据库操作//2发送MQ消息}这种情况下无法保证数据库操作和发送消息的一致性,因为数据库操作可能成功,发送消息如果失败,先发送消息,再操作数据库:publicvoidtest1(){//1发送MQ消息//2数据库操作}这种情况下,之间的一致性无法保证数据库操作和发送消息,因为消息可能发送成功,而数据库操作失败。在数据库事务中,先发送消息,再操作数据库:@Transactionalpublicvoidtest1(){//1发送MQ消息//2数据库操作}这里使用了spring的@Transactional注解,在操作中该方法都在一个事务中。一致性也不能保证,因为消息发送成功,如果操作数据库失败,回滚数据库操作,但是不能回滚MQ消息。在一个数据库事务中,先操作数据库,再发送消息:@Transactionalpublicvoidtest1(){//1数据库操作//2发送MQ消息}这样的话,貌似没有问题。如果发送MQ消息失败,抛出异常,事务将回滚(加上@Transactional注解后,spring方法抛出异常后,会自动回滚)。这只是一种错觉,因为如果是响应超时导致的异常,实际上发送MQ消息可能已经成功了。此时数据库操作还是回滚了,但是MQ消息实际上已经发送成功,导致不一致。使用JTA事务管理器:在启动事务的方法上加上spring的@Transactional注解。其实还有一个条件没有说清楚,就是我们配置的事务管理器是DataSourceTransactionManager。其实Spring还提供了另外一个分布式事务管理器JtaTransactionManager。这是为了使用XA两阶段提交来保证事务的一致性。当然前提是你的消息中间件实现了JMS规范中事务消息相关的API(回想一下我们前面介绍JTA规范的时候提到DB和MQ只是资源管理器RM。对于事务管理器来说,两个是等价的)。因此,如果满足两个条件:1、使用JtaTransactionManager2、DB、MQ实现JDBC和JMS规范中RM应该实现的两阶段提交API,可以保证消息发送的一致性。DB作为RM,一般都支持两阶段提交。但是有些MQ中间件不支持,所以需要找一个支持两阶段提交的MQ中间件。此外,JtaTransactionManager只是一个代理,您需要提供一个真正的TransactionManager(TM)实现。如前所述,atomikos公司有这样的产品。但是作者还是不建议这样做。由于XA两阶段提交性能较低,我们使用消息中间件进行异步解耦。这种情况下,虽然保证了一致性,但是响应时间大大增加,系统可用性降低。可靠发送消息的解决方案实现可靠消息发送有两种方式:基于MQ的事务消息和本地事务表。基于MQ的事务消息以RocketMQ的事务消息为例,如下图所示,消息的可靠发送由发送端的生产者保证(消费者无需考虑),发送可靠消息的步骤如下:发送事务消息,此时RocketMQ将消息状态标记为Prepared。注意这个消息消费者此时不能消费;执行业务代码逻辑,可以是本地数据库事务操作;确认发送消息,此时RocketMQ将消息状态标记为consumable,此时消费者才能真正保证消费到这条数据。如果确认信息发送失败怎么办?RocketMQ会定时扫描消息集群中的事务消息,如果发现有Prepared消息,会向消息发送者(生产者)确认。RocketMQ会根据发送方设置的策略决定是回滚还是继续发送确认消息。这样可以确保消息发送成功或失败与本地事务同时发生。消费失败怎么办?阿里给我们提供的解决方案是:手动解决。本地事务表并非所有mq都支持事务消息。即消息一旦发送到消息队列,消费者就可以立即消费。这时候可以使用独立的消息服务,或者本地事务表。可以看出,消息实际上是发给了一个我们自己写的“独立消息服务”应用。一开始是prepare状态。业务逻辑处理成功后,消息确认发送。这时,“独立消息服务”就会实际将消息发送到消息队列中。consumer消费成功后,acking时,除了对消息队列进行acking(图中未显示),还会对独立的消息服务进行ack。“独立消息服务”一般会删除这条消息。定时扫描prepare状态的消息,并向消息发送者(生产者)确认的工作,也是由独立的消息服务完成的。对于“本地事务表”,它类似于“独立消息服务”,只是“独立消息服务”需要独立部署,而“本地事务表”是将“独立消息服务”的功能嵌入到应用中中间。我是狐神,欢迎大家关注我的微信公众号:wzm2zsd参考文档灵活交易:可靠消息最终一致性