使用消息队列解决分布式事务一致性问题
如何可靠保存凭证(消息)
有两种方法:
业务与消息耦合的方式
支付宝在完成扣款的同时,同时记录消息数据,这个消息数据与业务数据保存在同一数据库实例里(消息记录表表名为message);
上述事务能保证只要支付宝账户里被扣了钱,消息一定能保存下来。 当上述事务提交成功后,我们通过实时消息服务将此消息通知余额宝,余额宝处理成功后发送回复成功消息,支付宝收到回复后删除该条消息数据。
业务与消息解耦方式
上述保存消息的方式使得消息数据和业务数据紧耦合在一起,从架构上看不够优雅,而且容易诱发其他问题。为了解耦,可以采用以下方式。 1)支付宝在扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送,只有消息发送成功后才会提交事务; 2)当支付宝扣款事务被提交成功后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送该消息; 3)当支付宝扣款事务提交失败回滚后,向实时消息服务取消发送。在得到取消发送指令后,该消息将不会被发送; 4)对于那些未确认的消息或者取消的消息,需要有一个消息状态确认系统定时去支付宝系统查询这个消息的状态并进行更新。为什么需要这一步骤,举个例子:假设在第2步支付宝扣款事务被成功提交后,系统挂了,此时消息状态并未被更新为“确认发送”,从而导致消息不能被发送。 优点:消息数据独立存储,降低业务系统与消息系统间的耦合; 缺点:一次消息发送需要两次请求;业务处理服务需要实现消息状态回查接口。
如何解决消息重复投递的问题
还有一个很严重的问题就是消息重复投递,以我们支付宝转账到余额宝为例,如果相同的消息被重复投递两次,那么我们余额宝账户将会增加2万而不是1万了。 为什么相同的消息会被重复投递?比如余额宝处理完消息msg后,发送了处理成功的消息给支付宝,正常情况下支付宝应该要删除消息msg,但如果支付宝这时候悲剧的挂了,重启后一看消息msg还在,就会继续发送消息msg。 解决方法很简单,在余额宝这边增加消息应用状态表(message_apply),通俗来说就是个账本,用于记录消息的消费情况,每次来一个消息,在真正执行之前,先去消息应用状态表中查询一遍,如果找到说明是重复消息,丢弃即可,如果没找到才执行,同时插入到消息应用状态表(同一事务)。
Ebay的研发人员早在2008年就提出了应用消息状态确认表来解决消息重复投递的问题:http://queue.acm.org/detail.cfm?id=1394128。
阅读更多- 使用kafka消息队列解决分布式事务(可靠消息最终一致性方案-本地消息服务)
- 使用消息队列来解决微服务数据一致性问题
- Linux下面使用消息队列通信出现*** stack smashing detected ***: ./pop3main terminated的解决方法
- RabbitMQ使用不当导致的队列堵塞问题及解决办法
- C#使用队列(Queue)解决简单的并发问题
- 使用事件和消息队列实现分布式事务
- 后台程序使用SystemV消息队列遇到的资源泄漏问题
- 使用AppDelegate单例,解决子视图无法给父视图发送消息的问题
- 微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务
- queue队列的建立及使用,约瑟夫问题C/C++的解决
- Java多线程之生产者消费者问题<三>:使用阻塞队列更优雅地解决生产者消费者问题
- 微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务
- 使用事件和消息队列实现分布式事务
- [转载]使用消息队列实现分布式事务-公认较为理想的分布式事务解决方案
- 使用阻塞队列解决生产者-消费者问题
- 使用Handler在线程之间传递消息所遇到的问题及解决方法
- (C#)使用队列(Queue)解决简单的并发问题
- PHP结合memcacheq消息队列解决并发问题
- 使用MSMQ消息队列的WCF的效率的问题。
- (C#)使用队列(Queue)解决简单的并发问题