rocketmq发送事务消息

现在有一个业务场景为了保证数据一致性,可以使用事务消息。

教师节学生给老师送花活动,先校验学生和老师是否是师生关系,再判断学生金币是否够,够则扣减学生金币,同时给老师增加相同的花数,对老师收到的鲜花进行全国排名,排名前十的可以获得平台赠送的实体书籍作为奖励。为了保证数据的一致性和高并发,扣减金币之后发送事务消息。

核心代码如下:

import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.message.Message;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.exception.MQClientException;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.SendResult;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.TransactionCheckListener;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.TransactionMQProducer;

public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {
  
        TransactionCheckListener transactionCheckListener = new TransactionCheckListenerImpl();
        TransactionMQProducer producer = new TransactionMQProducer("transaction_Producer");
        producer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
        // 事务回查最小并发数  
        producer.setCheckThreadPoolMinSize(2);  
        // 事务回查最大并发数  
        producer.setCheckThreadPoolMaxSize(2);  
        // 队列数  
        producer.setCheckRequestHoldMax(2000);  
        producer.setTransactionCheckListener(transactionCheckListener);  
        producer.start();  

        TransactionExecuterImpl tranExecuter = new TransactionExecuterImpl();  
        for (int i = 1; i <= 2; i++) {  
            try {  
                Message msg = new Message("TopicTransactionTest", "transaction" + i, "KEY" + i,  
                        ("Hello RocketMQ " + i).getBytes());  
                SendResult sendResult = producer.sendMessageInTransaction(msg, tranExecuter, null);
                System.out.println(sendResult);  
  
                Thread.sleep(10);  
            } catch (MQClientException e) {  
                e.printStackTrace();  
            }  
        }  
  
        for (int i = 0; i < 100000; i++) {  
            Thread.sleep(1000);  
        }  
  
        producer.shutdown();  
  
    }  
}  

 

TransactionExecuterImpl类用于执行本地事务,与消息的发送构成一个原子操作,要么发送消息,要么回滚。
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.LocalTransactionExecuter;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.message.Message;

public class TransactionExecuterImpl implements LocalTransactionExecuter {

    @Override
    public LocalTransactionState executeLocalTransactionBranch(Message msg, Object o) {
        System.out.println("执行本地事务msg = " + new String(msg.getBody()));

        //todo 执行本地事务,扣减学生金币,执行成功则提交,失败则回滚

        String tags = msg.getTags();

        if (tags.equals("transaction2")) {
            System.out.println("======我的操作============,失败了  -进行ROLLBACK");
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }

        //todo 本地写一个表t_message_transaction记录本次操作是提交还是回滚,方便回查操作

        return LocalTransactionState.COMMIT_MESSAGE;
    }
} 
本地事务操作的结果最好是记录在一个t_message_transaction表中,不然可能需要查很多表才确定本地事务执行成功与否。

TransactionCheckListenerImpl用于回查本地事务,代码如下:
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.producer.TransactionCheckListener;
import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.message.MessageExt;

public class TransactionCheckListenerImpl implements TransactionCheckListener {

    //在这里,我们可以根据由MQ回传的key去数据库查询,这条数据到底是成功了还是失败了。  
    public LocalTransactionState checkLocalTransactionState(MessageExt msg) {
        System.out.println("未决事务,服务器回查客户端msg =" + new String(msg.getBody().toString()));

        //todo 回查t_message_transaction表,确定事务最终状态

        // return LocalTransactionState.ROLLBACK_MESSAGE;
        return LocalTransactionState.COMMIT_MESSAGE;
        // return LocalTransactionState.UNKNOW;  
    }  
}

消费方在收到消息时,可以进行消费失败,为了保险起见,同一条消息没消费失败一次,可以在数据库将计数器加1,消费失败会累计16次重发,达到这个阈值可以触发报警向钉钉群里发消息,这里不再写消费端的代码了。

补充:

1. 分布式事务等于事务消息吗?

两者并没有关系,事务消息仅仅保证本地事务和MQ消息发送形成整体的原子性,而投递到MQ服务器后,消费者是否能一定消费成功是无法保证的。

延伸阅读,事务消息的源码解读:https://segmentfault.com/a/1190000019755235?utm_source=tag-newest

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值