(1)、基于数据库XA/JTA协议的方式;(须要数据库厂商的支持;java组件有atomikos等)java
(2)、异步校对数据的方式;(支付宝、微信支付主动查询支付转态、对帐单的形式)spring
(3)、基于可靠消息(MQ)的解决方案;(异步场景;通用性强;拓展性较高)数据库
(4)、TCC编程式解决方案。(严选、阿里、蚂蚁金服本身封装的DTX)编程
(1)、总体设计思路微信
要求:①、可靠生产:保证消息必定要发送到rabbitmq服务中运维
②、可靠消费:保证消息取出来必定正确消费掉。异步
最终多方数据达到一致性。分布式
发送消息到rabbitmq以后的回调方法修改消息发送的状态测试
设置发送成功的回调配置:spring.rabbitmq.publisher-confirm-type=CORRELATED微信支付
private static boolean IS_END = false; @Autowired private RabbitTemplate rabbitTemplate; @PostConstruct public void setUp(){ System.out.println("回调方法"); //回调方法 rabbitTemplate.setConfirmCallback((correlationData,ack,cause)->{ if (!ack){ //TODO 执行失败的逻辑 System.out.println("执行失败:"+cause); }else{ //TODO 执行成功的逻辑 System.out.println("执行成功"); } }); IS_END = true; } @Test public void test0(){ //发送消息 rabbitTemplate.convertAndSend("OrderDispathExchange",null,"测试sa"); System.out.println("发送完成"); do{}while (!IS_END); }
备注:若是出现回执没有收到或者状态修改失败等特殊状况,能够采用一下方案:定时检查消息表,超时没有发送成功的,再次发送。
幂等:根据ID或业务数据,判断数据是否重复(能够查看对应的数据表),重复则忽略。
消费者正常消费后,返回ACK状态到rabbitMq的服务端,进行数据的清除
消费者处理消息失败以后,须要MQ再次重发给消费者(通常会重试几回,由消费者自身记录重试次数,并进行次数控制)。
开启手动ack,控制消息在MQ中的删除、重发、丢弃
spring.rabbitmq.listener.simple.acknowledge-mode=manual
/** * * @param message 消息内容 * @param channel java和mq创建的链接通道 * @param tag 当前消息的标签 * @throws Exception */ @RabbitListener(queues = "OrderDispath") public void messageConsumer(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG)long tag)throws Exception{ try { //取出信息,执行相关业务,同一个数据不一样屡次处理,能够根据业务状况去重, // 保证幂等性(如:可使用readis记录每条数据处理次数或者使用主键进行数据库的插入) System.err.println(message); //..... //ack -反馈给mq,数据已经正常处理 channel.basicAck(tag,false); }catch (Exception e){ //捕获异常,通知mq重发 //可是必定要记录该条消息处理的次数,防止重试屡次,致使死循环 //对于重试屡次,或者明确不在继续重发的异常,通知mq丢弃,经过运维告急机制,通知人工处理 channel.basicNack(tag,false,false); } //生产环境,重要的数据出现异常,必须人工干预 //若是不回复,就等这个consumer断开链接以后,mq-server会继续推送信息 }
优势:通用性强,拓展性强,方案成熟。
缺点:基于消息中间件,只适合异步场景,不支持事务回滚(要求必须成功);消息处理会有延迟,须要业务上可以容忍。
备注:尽可能避免分布式事务;尽可能将非核心的事务作成异步。