使用RabbitMQ实现分布式事务

本地事务

系统规模较小,数据表都在一个数据库实例上,利用本地事务即可解决,当系统规模较大,不在同一数据库实例上,往往会分布在不同的物理节点上,本地事务无法解决

分布式事务 2PC 两阶段提交协议

  • 应用程序client,发起一个开始请求到TC(事务协调者)
  • TC将prepare消息写到本地日志,之后向所有的Si(事务执行者)发送prepare消息
  • Si收到消息后,执行本机事务,但不会进行commit,成功则返回yes,返回前将消息写到本地日志
  • TC收集所有SI返回的消息,所有执行器都返回yes,则给所有执行器发送commit消息,SI收到后执行本地事务commit操作;任一执行器返回no,那么所有执行器发送abort消息,执行器abort

问题: 多次节点间通信,时间过长;事务时间相对变长,锁定资源时间变长,资源浪费

消息队列避免分布式事务

可靠保存消息:

  • 业务与消息耦合的方式:业务在完成时,记录消息数据,将消息数据与业务数据保存在同一数据库实例中,业务提交成功后,将消息发送,收到消息成功回复,则删除该消息
  • 业务与消息解耦的方式:业务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,不发送,当业务提交成功之后,消息发送;业务事务被提交成功后,向实时消息服务确认发送,得到确认消息后,实时消息服务才发送该消息;业务事务提交失败回滚后,向实时消息服务取消发送,得到取消发送指令后,消息不会被发送

消息重复投递

  • 增加消息应用状态表来记录消息的消费情况,来一个消息,在执行之前,应该先去消息状态应用表中查询一次,找到则是重复消息,丢弃即可。

分布式事务解决方案

  • MQ消息中间件实现的可靠消息最终一致性
  • TCC补偿事务解决方案  trying-confirming-canceling
  • 最大努力通知型 与第三方系统通信

RabbitMQ消息重试机制

  • 消费者获取到消息后,调用第三方接口,但接口暂时无法访问,需要重试机制;消费者获取消息后,抛出异常则无需重试,需要对版本进行更新

解决幂等性

  • 网络延迟传输中,消费者出现异常或者是消费延迟消费,会造成MQ进行重试补偿
  • 使用全局MessageID 判断消费者使用同一个
  • 使用业务逻辑保证唯一(保证一个订单号码只可能被插入一次数据库)

RabbitMQ死信队列

  • 消息被拒绝;消息TLL过期;队列达到最大长度(无法添加数据到MQ中)变成死信之后,被重新投递到另一个Exchange上,该exchange根据绑定规则转发到对应队列上监听该队列,就可重新消费。
  • 定义业务队列时可以指定一个死信交换机,并绑定死信队列,消息变成死信时,就将该消息发送到该死信队列,

采用最终一致性原理:

  • 确认生产者一定要将数据投递MQ中(MQ消息确认机制)
  • MQ消费者消息能够正确消费消息,手动ACK
  • 第一个事务先执行,采用补偿机制,创建一个补单消费者进行监听,若第一个事务执行出错,补单消费者会重新执行
posted @ 2021-06-16 10:47  简直😓  阅读(1000)  评论(0编辑  收藏  举报