RabbitMQ可靠消息最终一致性
为什么要使用可靠消息最终一致性?
在实际的开发系统中,各个服务之间的调用可能是异步的,也就是说一个服务发消息给MQ然后,消费者从MQ中拿取消息。在这个过程中针对基于MQ的异步调用我们要保证分布式事务的原子性,要么一起成功,要么一起失败。所以这个时候就需要用上可靠消息最终一致性了。
什么是可靠消息最终一致性
通俗来讲就是上游服务需要保证消息一定会发出给MQ,下游服务一定会接收到消息,并且在成功消费掉消息之后,返回ack。这个方案是通过消息中间件完成的。
可靠消息最终一致性需要解决的问题
1 上游服务把信息成功发送
本地事务与消息发送的原子性问题:事务发起方在本地事务执行成功后消息必须发出去,否则就回滚事务。即实现本地事务和消息发送的原子性,要么都成功,要么都失败。
2 下游服务成把消息成功消费
事务参与方接收消息的可靠性:事务参与方必须能够从消息队列接收到消息。
3 对消息做幂等
消息重复消费的问题:由于网络2的存在,若某一个消费节点响应超时但是消费成功,此时消息中间件会重复投递此消息,就导致了消息的重复消费。
解决方案
问题 1:上游服务把消息成功发送
通过使用本地消息表来进行不断的扫描查看是否还有未发出的消息,通过quartz定时发送消息,如果发送成功,就讲本地消息表的状态改为成功发送状态或者删除。
问题2:消息中间件必须保证消息不丢失
消息中间件的持久化:
- MQ容器的持久化:声明的同时将durable属性设置为true
- exchange的持久化:声明的同时将durable属性设置为true
- Message的持久化:发送消息的时候,将basicProperties属性设置为MessageProperties.PERSISTENT_TEXT_PLAIN
问题3:下游服务成把消息成功消费
手动ack:保证消息投递失败时消息的重新投递
问题4:对消息做幂等
消息去重表:任务B处理消息前,先查询该消息是否被消费,如果没消费,处理任务B成功,记录消息。如果消息已经被消费,直接返回应答成功
附录1 本地消息表生成sql
DROP TABLE IF EXISTS `local_message`;
CREATE TABLE `local_message` (
`tx_no` varchar(255) NOT NULL,
`item_id` bigint DEFAULT NULL,
`state` int(11) DEFAULT NULL,
PRIMARY KEY (`tx_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
附录2 去重表生成sql
DROP TABLE IF EXISTS `msg_distinct`;
CREATE TABLE `msg_distinct` (
`tx_no` varchar(255) NOT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`tx_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;