使用RocketMQ实现分布式事务

RocketMQ半事物原理 可以百度

落地代码

1、首先添加以下工具类

@Configuration

public class RocketMQExecutorServiceConfig {

    private static final AtomicInteger counter = new AtomicInteger(1);

    private static class RocketMQThreadFactory implements ThreadFactory {

        @Override

        public Thread newThread(Runnable r) {

            Thread thread = new Thread(r);

            thread.setName("RocketMQThreadFactory."+counter.getAndIncrement());

            return thread;

        }

    }

    @Bean("rocketMQExecutorService")

    public ExecutorService executorService()

    {

        ExecutorService executorService = new ThreadPoolExecutor(

                8,

                24,

                60,

                TimeUnit.SECONDS,

                new ArrayBlockingQueue<Runnable>(1000),

                new RocketMQThreadFactory()

        );

        return executorService;

    }

}

LocalExecutor

public interface LocalExecutor<T> {

    void execute() throws Exception;

    T getResult();

    ResultDTO<T> resultDtoOrThrow() throws Exception;

}

EetMQProducer 

@Slf4j

@Configuration

public class EetMQProducer {

    private String namesrv;

    private String appName;

    @Autowired

    public ExecutorService executorService;

    @Autowired

    private EetEeartagBagAppService eetEeartagBagAppService;

    private EetTransactionRecordMapper eetTransactionRecordMapper;

    private BasePService<EetTransactionRecordMapper, EetTransactionRecordPo> eetTransactionRecordPoService;

    public EetMQProducer(EetTransactionRecordMapper eetTransactionRecordMapper)

    {

        this.eetTransactionRecordMapper = eetTransactionRecordMapper;

        this.eetTransactionRecordPoService = new BasePService<>(eetTransactionRecordMapper);

    }

    public class TransactionListenerImpl implements TransactionListener {

        @Override

        public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {

            try {

                LocalExecutor localExecutor = (LocalExecutor) arg;

                localExecutor.execute();

                return LocalTransactionState.COMMIT_MESSAGE;

            catch (Exception e) {

                return LocalTransactionState.ROLLBACK_MESSAGE;

            }

        }

        @Override

        public LocalTransactionState checkLocalTransaction(MessageExt msg) {

            EetTransactionRecordPo transactionRecordPo = eetTransactionRecordMapper.getByTxid(msg.getTransactionId());

            if( transactionRecordPo==null ){

                long timePassed = System.currentTimeMillis() - msg.getBornTimestamp();

                if( timePassed < 1000 60 ){

                    return LocalTransactionState.UNKNOW;

                else {

                    return LocalTransactionState.ROLLBACK_MESSAGE;

                }

            }

            if(StringUtils.equals(EetTransactionRecordStatusEnum.FINISHED.getStatus(), transactionRecordPo.getStatus()) ){

                return LocalTransactionState.COMMIT_MESSAGE;

            else {

                return LocalTransactionState.ROLLBACK_MESSAGE;

            }

        }

    }

    @Bean

    public TransactionMQProducer init() throws MQClientException {

        String group = "EetMQProducer-" + appName;

        TransactionListener transactionListener = new TransactionListenerImpl();

        TransactionMQProducer producer = new TransactionMQProducer(group);

        producer.setNamesrvAddr(namesrv);

        producer.setExecutorService(executorService);

        producer.setTransactionListener(transactionListener);

        producer.start();

        return producer;

    }

}

2、对service层的改造

1)将原来的service方法的逻辑包在try finally中,在最后添加事务记录信息。需要注意的是之前有些主键id是在service方法内生成的,现在由于发消息是在执行service方法之前,所以主键id需要在service调用之前生成再通过参数传进来。

 @Override

    public EetEeartagBagGrantResDto save(Long bagId, EetEeartagBagGrantReqDto reqDto, Message msg)

    {

        LocalDateTime startTime = LocalDateTime.now();

        Exception exception = null;

        try {

           ...

           return result;

        catch (Exception e) {

            exception = e;

            throw e;

        finally {

            if( exception==null ){

                eetTransactionRecordAppService.save(msg.getTransactionId(), startTime, EetTransactionRecordStatusEnum.FINISHED.getStatus(), reqDto);

            }

        }

2)将原来对service层的调用封装到LocalExecutor中

@Component

public class EetEeartagBagCreatedLocalExecutorFactory {

    @Autowired

    private EetEeartagBagAppService eetEeartagBagAppService;

    public LocalExecutor create(Long bagId, EetEeartagBagGrantReqDto reqDto, Message msg)

    {

        return new EetEeartagBagCreatedLocalExecutor(bagId, reqDto, msg);

    }

    public class EetEeartagBagCreatedLocalExecutor implements LocalExecutor<EetEeartagBagGrantResDto>

    {

        private Long bagId;

        private EetEeartagBagGrantReqDto reqDto;

        private Message msg;

        private EetEeartagBagGrantResDto result;

        private Exception exception;

        public EetEeartagBagCreatedLocalExecutor(Long bagId, EetEeartagBagGrantReqDto reqDto, Message msg)

        {

            this.bagId = bagId;

            this.reqDto = reqDto;

            this.msg = msg;

        }

        @Override

        public void execute() {

            try {

                result = eetEeartagBagAppService.save(bagId, reqDto, msg);

            catch (Exception e) {

                exception = e;

                throw e;

            }

        }

        @Override

        public EetEeartagBagGrantResDto getResult() {

            return result;

        }

        @Override

        public ResultDTO<EetEeartagBagGrantResDto> resultDtoOrThrow() throws Exception {

            if( exception!=null ){

                throw exception;

            }

            return ResultDTO.ok(result);

        }

    }

}

3、对controller层的改造

注入TransactionMQProducer 

@Autowired

private TransactionMQProducer transactionMQProducer;

2)之前在controller是直接调用service的方法,现在需要修改为创建LocalExecutor,然后发送半事务消息的时候将LocalExecutor做为第二个参数,这样LocalExecutor就可以在半事务消息的执行本地事务阶段被执行,然后LocalExecutor还可以获取service执行方法的结果(或异常信息)

@PostMapping(consumes = {"application/json"}, produces = {"application/json"})

public ResultDTO<EetEeartagBagGrantResDto> addEetEeartagBag(

        @ApiParam(value = "要保存的对象", required = true@Valid @RequestBody EetEeartagBagGrantReqDto reqDto) throws Exception {

    long bagId = snowflakeIdWorker.nextId();

    JSONObject json = new JSONObject();

    json.put("bagId", bagId);

    Message msg = new Message(EetMQTopics.eet_eeartag_bag_created, "", json.toJSONString().getBytes("UTF-8"));

    LocalExecutor localExecutor = eetEeartagBagCreatedLocalExecutorFactory.create(bagId, reqDto, msg);

    transactionMQProducer.sendMessageInTransaction(msg, localExecutor);

    return localExecutor.resultDtoOrThrow();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值