分布式事务解决方案 | Seata | 本地消息表 | 事务消息 | 最大努力通知 | 消息丢失重复消费堆积有序

Posted 做猪呢,最重要的是开森啦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式事务解决方案 | Seata | 本地消息表 | 事务消息 | 最大努力通知 | 消息丢失重复消费堆积有序相关的知识,希望对你有一定的参考价值。

关于分布式事务的基本理论(2PC、3PC等)可以参考链接

1. 解决方案:

主要有seata、基于MQ的最终一致性方案(包括本地消息表、事务消息以及最大努力通知)

1.1. Seata:
  • Seata 是一款开源的分布式事务解决方案,提供了 AT、TCC、SAGA 和 XA 事务模式;
  • AT和XA模式都是对业务无侵入的,关于AT模式可以见参考链接
  • 系统并发量不大的情况,可以使用seata来解决分布式事务,但要是高并发下,性能较低,不适合使用
1.2. 本地消息表方案:
  • 方案的核心是通过本地事务保证数据业务操作和消息的一致性
  • 然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将消息删除

 定时任务的两种处理:

  • 1).扫描本地消息表未发送的消息,把参数拿出来,直接微服务feign调用,当然要捕获异常;如果正常执行完,再把表消息状态改为成功
  • 2).扫描本地消息表未发送的消息,发送普通MQ消息,消费端订阅消费;
  • 消息频繁发送失败/调用失败,可以根据业务采用定制的重试,比如间隔递增发送,失败N次,直接更新表消息状态为失败,然后人工干预等
  • MQ消费端监听要手动返回ack(也就是ConsumeConcurrentlyStatus)
  • 如果消费失败,会自动进行重试,默认重试16次,之后会进入死信队列DLQ,这种一般要人工干预处理
  • 当然,避免消费端重复消费,要做好接口的幂等性
1.3. RocketMQ事务消息方案:
  • RocketMQ 4.3之后的版本正式支持事务消息,其原理是通过二段提交和事务状态补偿思想来实现的

 二阶段提交:

  • 1)生产者先发送事务消息到broke服务,这时的消息是半消息,不会被消费者消费
  • 2)Broke服务收到半消息后会返回ok状态
  • 3)然后生产者进行本地事务,比如数据落库等
  • 4)本地事务执行完,也需要返回事务状态
  • 5)broke服务根据事务状态执行半消息的commit或rollback,如果commit,则半消息就能被消费者消费,rollback的话就会删除半消息
    ·

 事务状态补偿(回查):

  • 本地事务执行完,因网络波动,导致本地事务状态无法被broke服务感知到
  • 会有个定时任务,会发消息回查生产者的本地事务状态,再根据返回的本地事务状态进行提交或回滚或继续回查
  • 默认回查的超时时间为6秒,最大的回查次数为15秒次,超过15次就会回滚

 实现要点:

  • producer.sendMessageInTransaction发送事务消息
  • 生产者消息监听类实现TransactionListener,重写executeLocalTransaction、checkLocalTransaction方法
  • executeLocalTransaction:执行本地事务;checkLocalTransaction消息事务回查
  • 消息监听实现类,添加@RocketMQTransactionListener注解,或者生产者producer.setTransactionListener(listener);

1.4. 最大努力通知方案:
  • 就是调用方最大努力去通知通知方,如果通知不到,就需要通知方主动调用调用方的数据校对接口进行校对
  • 比如注册用户送优惠券,在用户服务添加用户后调用优惠券服务发放优惠券(添加新用户优惠券类型数据)
  • 个人感觉本地事务表方案也算最大努力了,努力的发消息去进行事务操作;
  • 我的理解最大努力通知方案更多作为操作反馈,比如积分添加完了,我要反馈通知订单服务我已经加上积分了

 举栗:

  • 用户服务添加用户后发送事务消息到优惠券服务,优惠券服务监听消费消息进行发放优惠券后,发送普通MQ消息通知用户服务
  • 用户服务监听到消息更新用户优惠券发放状态
  • 如果发送MQ消息失败,则间隔递增时间重试,超过设定N次就不重试了,就需要人工干预了
  • 用户服务需要调用优惠券服务数据校对接口进行校对(采用定时或其他方式)
1.5. 小结:
  • 像基于MQ消息的本地事务表和消息事务,都是保证消息和本地事务原子性并且能正常发送到消费端
  • 但对消费端消费异常就没有做更多的处理,所以实际还需要根据异常做事务补偿
  • 而seata的全局事务也能保证事务最终一致性,原理也是通过undo_log做的事务补偿,但高并发下性能好像不是很理想
  • 不管是哪种解决方案,都不会完美解决分布式事务问题,只是根据不同业务场景在性能、一致性、可用性等方面做取舍
  • 像强一致性可以用seata的XA模式,但性能和可用性就降低;

2. RocketMQ相关问题:

2.1. RocketMq消息丢失怎么办:
  • 如果是生产者端因网络波动而丢失消息,可以采用事务消息进行发送,其原理是通过二段提交和事务状态补偿思想来实现的;见上文
  • 如果是broke储存消息时丢失,那么可以将消息刷盘改为同步刷盘,当然会有性能影响;如果有必要还可以集群部署,保证数据备份
  • 如果是消费者端丢失,我们可以在监听器中成功消费后再手动确认状态SUCCESS返回;
  • 消息消费失败会进行重试,默认16次后进入死信队列,有必要的话也可以将DLQ的消息取出来消费,或人工干预
2.2. RocketMq消息重复消费怎么办:
  • 很难保证生产者不重复提交消息,所以我们要尽可能的保证消费者消费业务的幂等性
  • 像数据落库的话,可以在数据库层面建立唯一索引约束;
  • 或者将消息缓存redis/本地表,再进行业务判断来处理相同的消息直接成功
2.3. RocketMq消息堆积怎么办:
  • 能扩充消费者就扩充消费者先消费,同时排查业务逻辑有没有阻塞消费
2.4. RocketMq保证消息有序性:
  • 如果只是保证某一业务的消息有序,我们可以发消息的时候指定MessageQueueSelector来实现队列的选择,根据业务标识放入同一队列
  • 如果要严格保证有序,那只能使用一个queue,严格按照先入先出的顺序消费,会牺牲很大的并发性能

以上是关于分布式事务解决方案 | Seata | 本地消息表 | 事务消息 | 最大努力通知 | 消息丢失重复消费堆积有序的主要内容,如果未能解决你的问题,请参考以下文章

seata 分布式事务解决方案汇总

最后的分布式事务 有用

多数据源下Seata分布式事务出现的问题和解决方法

多数据源下Seata分布式事务出现的问题和解决方法

多数据源下Seata分布式事务出现的问题和解决方法

多数据源下Seata分布式事务出现的问题和解决方法