• RocketMQ 整合SpringBoot发送事务消息


    环境

    jdk: 8u22
    rocketmq: rocketmq-all-4.5.2-bin-release
    springboot: 2.1.6.RELEASE
    rocketmq-springboot: 2.0.3 

    发送流程(事务消息)

    Rocket发送事务消息:

    Rocket发送事务消息是二次提交的,第一次发送prepare提交到服务器时消息主题会替换为RMQ_SYS_TRANS_HALF_TOPIC。等到本地事务执行完毕以后才进行二次提交,这时会发送给原本消息的topic。

    1、由producer发送prepare(半消息)给MQ的broker。MQ会把消息记录到本地,然后回复prepare消息状态给producer。

    2、prepare消息发送以后获取发送状态,如果是成功则执行本地业务(本地事务),根据本地事务执行结果手动返回相应状态(RocketMQLocalTransactionState.COMMIT、RocketMQLocalTransactionState.ROLLBACK等)给MQ。

    3、如果是COMMIT则说明本地事务执行成功,prepare为可提交状态,MQ收到COMMIT消息就会发送给consumer,然后consumer执行本地业务。如果是ROLLBACK则会删除prepare消息。

    4、如果MQ一直没收到返回状态则会启动定时任务检查本地事务状态

    5、消费者、生产者的事务各由开发者自己保证。MQ的事务是由MQ保证,这里会根据使用者配置的参数来决定如何执行。

    生产者

    发送事务消息

    生产者

    /**
     * @author Zhang Qiang
     * @date 2019/12/4 15:55
     */
    @Slf4j
    @Service
    public class SyncProducer {
    
        @Resource
        private RocketMQTemplate rocketMQTemplate;
    
        public TransactionSendResult sendSyncMessage(String msg, String topic, String tag){
            log.info("【发送消息】:{}", msg);
            Message message = MessageBuilder.withPayload(msg).build();
            TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(MQGroup.SPRING_BOOT_PRODUCER_GROUP.getGroup(), topic, message, tag);
            log.info("【发送状态】:{}", result.getLocalTransactionState());
            return result;
        }
    
    }
    

    监听生产者发送事务消息

    每次推送消息会执行executeLocalTransaction方法,首先会发送半消息,到这里的时候是执行具体本地业务,执行成功后手动返回RocketMQLocalTransactionState.COMMIT状态,这里是保证本地事务执行成功,如果本地事务执行失败则可以返回ROLLBACK进行消息回滚。 此时消息只是被保存到broker,并没有发送到topic中,broker会根据本地返回的状态来决定消息的处理方式。

    checkLocalTransaction方法是rocket定时回查时所执行的,本环境下并没有执行,原因版本原因,是因为当前版本没有执行回调(待验证)。

    /**
     * @author Zhang Qiang
     * @date 2019/12/4 16:07
     */
    @Slf4j
    @Component
    @RocketMQTransactionListener(txProducerGroup = "spring_boot_producer_group")
    public class SyncProducerListener implements RocketMQLocalTransactionListener {
        private AtomicInteger trnner = new AtomicInteger(0);
        private ConcurrentHashMap<String, Object> localTrans = new ConcurrentHashMap<>();
        @Autowired
        private LocalService localService;
        @Override
        public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {
            try {
                localService.executeLocalService(message.getPayload().toString());
                log.info("【本地业务执行完毕】 msg:{}, Object:{}", message, o);
                localTrans.put(message.getHeaders().getId()+"", message.getPayload());
                return RocketMQLocalTransactionState.COMMIT;
            } catch (Exception e) {
                e.printStackTrace();
                log.error("【执行本地业务异常】 exception message:{}", e.getMessage());
                return RocketMQLocalTransactionState.ROLLBACK;
            }
        }
        @Override
        public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
            log.info("【执行检查任务】");
            return RocketMQLocalTransactionState.UNKNOWN;
        }
    }
    

    消费者

    这里注解可以使用selectorExpression = "tag",来标明tag,注意这里如果队列发送的时候当前消费者无法消费的时候消息就会发送给其它tag,也就是说tag并不能保证一定能消费到消息。

    /**
     * @author Zhang Qiang
     * @date 2019/12/4 16:07
     */
    @Slf4j
    @Component
    @RocketMQMessageListener(topic = "springboot_topic", consumerGroup = "spring_boot_consumer_group")
    public class SyncConsumer implements RocketMQListener<MessageExt>, RocketMQPushConsumerLifecycleListener {
    
        @Autowired
        ConsumerService service;
    
        @Override
        public void prepareStart(DefaultMQPushConsumer defaultMQPushConsumer) {
            defaultMQPushConsumer.registerMessageListener((MessageListenerConcurrently)(exts, context) ->{
                try {
                    log.info("【获取消息】");
                    if (!CollectionUtils.isEmpty(exts)) {
                        exts.forEach(ext->{
                            Integer con = ext.getReconsumeTimes();
                            service.readMessage(new String(ext.getBody()));
                            log.info("【消费消息】 次数:{}, ext :{}", con, ext);
                        });
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                } catch (Exception e){
                    e.printStackTrace();
                    log.error("【消费消息失败】,message:{}", e.getMessage());
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                }
            });
        }
    
        @Override
        public void onMessage(MessageExt messageExt) {
            String msg = null;
            try {
                msg = new String(messageExt.getBody(),"utf-8");
                log.info("MsgConsumer >>> {}, msgId:{}", msg, messageExt.getMsgId());
                service.readMessage(msg);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                log.error("【消费异常】:{}", e.getMessage());
            }
        }
    
    
    }
    

      

    执行结果:

    2019-12-04 17:36:43.711  INFO 9792 --- [io-10100-exec-3] com.zang.rocket.producer.SyncProducer    : 【发送状态】:COMMIT_MESSAGE
    2019-12-04 17:36:59.956  INFO 9792 --- [io-10100-exec-5] com.zang.rocket.producer.SyncProducer    : 【发送消息】:farst
    2019-12-04 17:36:59.958  INFO 9792 --- [io-10100-exec-5] com.zang.rocket.service.LocalService     : 【执行业务,读取发送消息】:[B@5dc81a2c
    2019-12-04 17:36:59.958  INFO 9792 --- [io-10100-exec-5] c.z.r.producer.SyncProducerListener      : 【本地业务执行完毕】 msg:GenericMessage [payload=byte[5], headers={rocketmq_TOPIC=springboot_topic, rocketmq_FLAG=0, id=252178bf-1d37-2f33-0892-721a12c0fc23, rocketmq_TRANSACTION_ID=C0A80A8B264018B4AAC2133ACA340005, timestamp=1575452219958}], Object:springboot_tag
    2019-12-04 17:36:59.958  INFO 9792 --- [io-10100-exec-5] com.zang.rocket.producer.SyncProducer    : 【发送状态】:COMMIT_MESSAGE
    2019-12-04 17:37:19.508  INFO 9792 --- [io-10100-exec-7] com.zang.rocket.producer.SyncProducer    : 【发送消息】:oneworld
    2019-12-04 17:37:19.509  INFO 9792 --- [io-10100-exec-7] com.zang.rocket.service.LocalService     : 【执行业务,读取发送消息】:[B@532e2914
    2019-12-04 17:37:19.510  INFO 9792 --- [io-10100-exec-7] c.z.r.producer.SyncProducerListener      : 【本地业务执行完毕】 msg:GenericMessage [payload=byte[8], headers={rocketmq_TOPIC=springboot_topic, rocketmq_FLAG=0, id=e9f68709-929a-acc9-bc90-426dc299fc5e, rocketmq_TRANSACTION_ID=C0A80A8B264018B4AAC2133B16940006, timestamp=1575452239509}], Object:springboot_tag
    2019-12-04 17:37:19.510  INFO 9792 --- [io-10100-exec-7] com.zang.rocket.producer.SyncProducer    : 【发送状态】:COMMIT_MESSAGE
    2019-12-04 17:37:42.620  INFO 9792 --- [io-10100-exec-9] com.zang.rocket.producer.SyncProducer    : 【发送消息】:好快!
    2019-12-04 17:37:42.622  INFO 9792 --- [io-10100-exec-9] com.zang.rocket.service.LocalService     : 【执行业务,读取发送消息】:[B@1addc4e0
    2019-12-04 17:37:42.622  INFO 9792 --- [io-10100-exec-9] c.z.r.producer.SyncProducerListener      : 【本地业务执行完毕】 msg:GenericMessage [payload=byte[7], headers={rocketmq_TOPIC=springboot_topic, rocketmq_FLAG=0, id=088cfba8-7b49-6ecd-8c7d-1c4b3e543f7d, rocketmq_TRANSACTION_ID=C0A80A8B264018B4AAC2133B70DC0007, timestamp=1575452262622}], Object:springboot_tag
    2019-12-04 17:37:42.622  INFO 9792 --- [io-10100-exec-9] com.zang.rocket.producer.SyncProducer    : 【发送状态】:COMMIT_MESSAGE
    2019-12-04 17:38:00.541  INFO 9792 --- [io-10100-exec-2] com.zang.rocket.producer.SyncProducer    : 【发送消息】:事务消息!
    2019-12-04 17:38:00.543  INFO 9792 --- [io-10100-exec-2] com.zang.rocket.service.LocalService     : 【执行业务,读取发送消息】:[B@2c4f2e8d
    2019-12-04 17:38:00.543  INFO 9792 --- [io-10100-exec-2] c.z.r.producer.SyncProducerListener      : 【本地业务执行完毕】 msg:GenericMessage [payload=byte[13], headers={rocketmq_TOPIC=springboot_topic, rocketmq_FLAG=0, id=b0fb1bcd-723a-bcac-d650-08eeff193762, rocketmq_TRANSACTION_ID=C0A80A8B264018B4AAC2133BB6DD0008, timestamp=1575452280543}], Object:springboot_tag
    
    

    消费结果

    2019-12-04 17:36:59.966  INFO 30024 --- [MessageThread_3] com.zang.rocket.cunsumer.SyncConsumer    : 【获取消息】
    2019-12-04 17:36:59.966  INFO 30024 --- [MessageThread_3] com.zang.rocket.service.ConsumerService  : 【读取消息服务】:farst
    2019-12-04 17:36:59.966  INFO 30024 --- [MessageThread_3] com.zang.rocket.cunsumer.SyncConsumer    : 【消费消息】 次数:0 ext :MessageExt [queueId=1, storeSize=316, queueOffset=0, sysFlag=8, bornTimestamp=1575452219956, bornHost=/192.168.10.139:60014, storeTimestamp=1575452219960, storeHost=/192.168.10.139:10911, msgId=C0A80A8B00002A9F0000000003C8971E, commitLogOffset=63477534, bodyCRC=1472579256, reconsumeTimes=0, preparedTransactionOffset=63477210, toString()=Message{topic='springboot_topic', flag=0, properties={MIN_OFFSET=0, REAL_TOPIC=springboot_topic, MAX_OFFSET=1, TRAN_MSG=true, CONSUME_START_TIME=1575452219966, id=b63e043d-d3d6-59ad-b455-bbc02add83e0, UNIQ_KEY=C0A80A8B264018B4AAC2133ACA340005, WAIT=false, PGROUP=spring_boot_producer_group, timestamp=1575452219956, REAL_QID=1}, body=[102, 97, 114, 115, 116], transactionId='C0A80A8B264018B4AAC2133ACA340005'}]
    2019-12-04 17:37:19.518  INFO 30024 --- [MessageThread_4] com.zang.rocket.cunsumer.SyncConsumer    : 【获取消息】
    2019-12-04 17:37:19.518  INFO 30024 --- [MessageThread_4] com.zang.rocket.service.ConsumerService  : 【读取消息服务】:oneworld
    2019-12-04 17:37:19.518  INFO 30024 --- [MessageThread_4] com.zang.rocket.cunsumer.SyncConsumer    : 【消费消息】 次数:0 ext :MessageExt [queueId=8, storeSize=319, queueOffset=0, sysFlag=8, bornTimestamp=1575452239508, bornHost=/192.168.10.139:60014, storeTimestamp=1575452239512, storeHost=/192.168.10.139:10911, msgId=C0A80A8B00002A9F0000000003C89A2D, commitLogOffset=63478317, bodyCRC=319562353, reconsumeTimes=0, preparedTransactionOffset=63477990, toString()=Message{topic='springboot_topic', flag=0, properties={MIN_OFFSET=0, REAL_TOPIC=springboot_topic, MAX_OFFSET=1, TRAN_MSG=true, CONSUME_START_TIME=1575452239518, id=9c9acec6-1fd4-d45c-1bc2-bc489e08e8bc, UNIQ_KEY=C0A80A8B264018B4AAC2133B16940006, WAIT=false, PGROUP=spring_boot_producer_group, timestamp=1575452239508, REAL_QID=8}, body=[111, 110, 101, 119, 111, 114, 108, 100], transactionId='C0A80A8B264018B4AAC2133B16940006'}]
    2019-12-04 17:37:42.634  INFO 30024 --- [MessageThread_5] com.zang.rocket.cunsumer.SyncConsumer    : 【获取消息】
    2019-12-04 17:37:42.634  INFO 30024 --- [MessageThread_5] com.zang.rocket.service.ConsumerService  : 【读取消息服务】:好快!
    2019-12-04 17:37:42.634  INFO 30024 --- [MessageThread_5] com.zang.rocket.cunsumer.SyncConsumer    : 【消费消息】 次数:0 ext :MessageExt [queueId=6, storeSize=318, queueOffset=0, sysFlag=8, bornTimestamp=1575452262620, bornHost=/192.168.10.139:60014, storeTimestamp=1575452262624, storeHost=/192.168.10.139:10911, msgId=C0A80A8B00002A9F0000000003C89D3E, commitLogOffset=63479102, bodyCRC=1129015149, reconsumeTimes=0, preparedTransactionOffset=63478776, toString()=Message{topic='springboot_topic', flag=0, properties={MIN_OFFSET=0, REAL_TOPIC=springboot_topic, MAX_OFFSET=1, TRAN_MSG=true, CONSUME_START_TIME=1575452262634, id=92e37d9b-b7b5-6064-65e5-deee043a5c48, UNIQ_KEY=C0A80A8B264018B4AAC2133B70DC0007, WAIT=false, PGROUP=spring_boot_producer_group, timestamp=1575452262620, REAL_QID=6}, body=[-27, -91, -67, -27, -65, -85, 33], transactionId='C0A80A8B264018B4AAC2133B70DC0007'}]
    2019-12-04 17:38:00.549  INFO 30024 --- [MessageThread_6] com.zang.rocket.cunsumer.SyncConsumer    : 【获取消息】
    2019-12-04 17:38:00.549  INFO 30024 --- [MessageThread_6] com.zang.rocket.service.ConsumerService  : 【读取消息服务】:事务消息!
    2019-12-04 17:38:00.549  INFO 30024 --- [MessageThread_6] com.zang.rocket.cunsumer.SyncConsumer    : 【消费消息】 次数:0 ext :MessageExt [queueId=13, storeSize=325, queueOffset=0, sysFlag=8, bornTimestamp=1575452280541, bornHost=/192.168.10.139:60014, storeTimestamp=1575452280544, storeHost=/192.168.10.139:10911, msgId=C0A80A8B00002A9F0000000003C8A055, commitLogOffset=63479893, bodyCRC=880426184, reconsumeTimes=0, preparedTransactionOffset=63479560, toString()=Message{topic='springboot_topic', flag=0, properties={MIN_OFFSET=0, REAL_TOPIC=springboot_topic, MAX_OFFSET=1, TRAN_MSG=true, CONSUME_START_TIME=1575452280549, id=1bc5beda-d52a-9fa3-3847-e301a28095b8, UNIQ_KEY=C0A80A8B264018B4AAC2133BB6DD0008, WAIT=false, PGROUP=spring_boot_producer_group, timestamp=1575452280541, REAL_QID=13}, body=[-28, -70, -117, -27, -118, -95, -26, -74, -120, -26, -127, -81, 33], transactionId='C0A80A8B264018B4AAC2133BB6DD0008'}]
    

      

    源码地址

    https://gitee.com/ghostsugar/rocketMq

  • 相关阅读:
    牛客代码测试栈深度
    "Coding Interview Guide" -- 在行列都排好序的矩阵中找数
    "Coding Interview Guide" -- 括号字符串的有效性和最长有效长度
    "Coding Interview Guide" -- 将正方形矩阵顺时针转动90°
    "Coding Interview Guide" -- 按照左右半区的方式重新组合单链表
    "Coding Interview Guide" -- 先序、中序和后序数组两两结合重构二叉树
    "Coding Interview Guide" -- 只用位运算不用算术运算实现整数的加减乘除运算
    "Coding Interview Guide" -- 从N个数中等概率打印M个数
    "Coding Interview Guide" -- 判断字符数组中是否所有的字符都只出现过一次
    "Coding Interview Guide" -- 字符串的统计字符串
  • 原文地址:https://www.cnblogs.com/meijsuger/p/11984597.html
Copyright © 2020-2023  润新知