• Springboot整合RocketMQ简单使用


      简单研究下Springboot 整合RocketMQ。 使用的是Apache的rocketmq-spring-boot-starter

    1. 初始化项目

    1. pom 文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud</artifactId>
            <groupId>cn.qz.cloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-rocketmq-provider</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.rocketmq</groupId>
                <artifactId>rocketmq-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    
    </project>

    2. application.properties

    server.port=8093
    spring.application.name=rocketmq-producer
    
    rocketmq.name-server=192.168.13.111:9876;192.168.13.112:9876
    rocketmq.producer.group=my-group1
    rocketmq.producer.tls-enable=false
    # 指定超时时间,默认是3 s
    rocketmq.producer.sendMessageTimeout=300000
    
    rocketmq.consumer.group=my-group1
    rocketmq.consumer.topic=spring-string
    
    demo.rocketmq.extNameServer=192.168.13.111:9876;192.168.13.112:9876

    2. 代码

    1. Application

    package cn.qz;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    
    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }

    2. 生产者

    package cn.qz.producer;
    
    import cn.qz.constants.MQConstants;
    import cn.qz.consumer.ObjectConsumerWithReply;
    import cn.qz.model.User;
    import org.apache.rocketmq.client.producer.SendCallback;
    import org.apache.rocketmq.client.producer.SendResult;
    import org.apache.rocketmq.spring.core.RocketMQLocalRequestCallback;
    import org.apache.rocketmq.spring.core.RocketMQTemplate;
    import org.apache.rocketmq.spring.support.RocketMQHeaders;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.messaging.Message;
    import org.springframework.messaging.MessageHeaders;
    import org.springframework.messaging.MessagingException;
    import org.springframework.messaging.support.MessageBuilder;
    import org.springframework.util.MimeTypeUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.ArrayList;
    import java.util.List;
    
    @RestController
    public class TestController {
    
        @Autowired
        private RocketMQTemplate rocketMQTemplate;
    
        /**
         * 如果使用其他数据源的rocketTemplate,需要指定名称
         */
        @Resource(name = "extRocketMQTemplate")
        private RocketMQTemplate extRocketMQTemplate;
    
        @GetMapping("/test1")
        public String test1() {
            sendBatch();
            return "test1";
        }
    
        @GetMapping("/test2")
        public String test2() {
            sendString();
            return "test2";
        }
    
        @GetMapping("/test3")
        public String test3() {
            sendSelfObject();
            return "test3";
        }
    
        @GetMapping("/test4")
        public String test4() {
            testRocketMQTemplateTransaction();
            return "test4";
        }
    
        @GetMapping("/test5")
        public String test5() {
            testSendAndReceive();
            return "test5";
        }
    
        @GetMapping("/test6")
        public String test6() {
            testSelfAck();
            return "test6";
        }
    
        /**
         * 测试手动回复应答消息
         */
        private void testSelfAck() {
            String stringSelfAck = MQConstants.STRING_SELF_ACK;
            for (int i = 0; i < 10; i++) {
                rocketMQTemplate.convertAndSend(stringSelfAck + ":tag" + i, "this is self ack " + i);
            }
        }
    
        /**
         * 发送具有回传消息的消息。 要求消费者实现 RocketMQReplyListener 接收并回复消息
         *
         * @see ObjectConsumerWithReply
         */
        private void testSendAndReceive() {
            String objectRequestTopic = MQConstants.SPRING_RECEIVE_OBJ;
            // Send request in async mode and receive a reply of User type.
            rocketMQTemplate.sendAndReceive(objectRequestTopic, new User().setUserAge((byte) 9).setUserName("requestUserName"), new RocketMQLocalRequestCallback<User>() {
                @Override
                public void onSuccess(User message) {
                    System.out.printf("send user object and receive %s %n", message.toString());
                }
    
                @Override
                public void onException(Throwable e) {
                    e.printStackTrace();
                }
            }, 5000);
        }
    
        private void testRocketMQTemplateTransaction() throws MessagingException {
            String springTransTopic = MQConstants.SPRING_TRANS_TOPIC;
            String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
            for (int i = 0; i < 10; i++) {
                try {
                    Message msg = MessageBuilder.withPayload("rocketMQTemplate transactional message " + i).
                            setHeader(RocketMQHeaders.TRANSACTION_ID, "KEY_" + i).build();
                    SendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                            springTransTopic + ":" + tags[i % tags.length], msg, null);
                    System.out.printf("------rocketMQTemplate send Transactional msg body = %s , sendResult=%s %n",
                            msg.getPayload(), sendResult.getSendStatus());
                    Thread.sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 发送自定义对象携带自定义的header
         */
        private void sendSelfObject() {
            Message<User> build = MessageBuilder.withPayload(
                    new User().setUserAge((byte) 21).setUserName("sendSelfObject"))
                    .setHeader("MY_HEADER", "MY_VALUE")
                    .setHeader(RocketMQHeaders.KEYS, "key1")
                    .build();
            rocketMQTemplate.syncSend(MQConstants.SELF_TOPIC, build);
        }
    
        private void sendBatch() {
            /**
             * 发送批量消息
             */
            String topic = MQConstants.STRING_BATCH;
            String tags[] = new String[]{"tag0", "tag1", "tag2", "tag3"};
            List<Message> msgs1 = new ArrayList<Message>();
            for (int i = 0; i < 10; i++) {
                msgs1.add(MessageBuilder.withPayload("Hello RocketMQ Batch Msg#" + i)
                        .setHeader(RocketMQHeaders.KEYS, "KEY_" + i)
                        .build());
            }
            SendResult sr = rocketMQTemplate.syncSend(topic, msgs1, 60000);
            System.out.println("--- Batch messages send result :" + sr);
    
            /**
             * 发送批量有序性消息, 根据key 来选择队列
             */
            topic = MQConstants.STRING_BATCH_ORDER;
            for (int q = 0; q < 4; q++) {
                // send to 4 queues
                List<Message> msgs = new ArrayList<Message>();
                for (int i = 0; i < 10; i++) {
                    int msgIndex = q * 10 + i;
                    String msg = String.format("Hello RocketMQ Batch Msg#%d to queue: %d", msgIndex, q);
                    msgs.add(MessageBuilder.withPayload(msg).
                            setHeader(RocketMQHeaders.KEYS, "KEY_" + msgIndex).build());
                }
                sr = rocketMQTemplate.syncSendOrderly(topic, msgs, q + "", 60000);
                System.out.println("--- Batch messages orderly to queue :" + sr.getMessageQueue().getQueueId() + " send result :" + sr);
            }
        }
    
        private void sendString() {
            String springTopic = MQConstants.STRING_TOPIC;
            String delayTopic = MQConstants.DELAY_TOPIC;
            String userTopic = MQConstants.USER_TOPIC;
    
            /***********第一种发送方式*********/
            // 同步发送消息。默认重复两次。不指定超时时间会拿producer 全局的默认超时时间(默认3s)
            SendResult sendResult = rocketMQTemplate.syncSend(springTopic, "Hello, World!");
            System.out.printf("syncSend1 to topic %s sendResult=%s %n", springTopic, sendResult);
            // 指定超时时间是10 s
            sendResult = rocketMQTemplate.syncSend(springTopic, "Hello, World2!", 10 * 1000);
            System.out.printf("syncSend2 to topic %s sendResult=%s %n", springTopic, sendResult);
            // 发送消息并且指定tag
            sendResult = rocketMQTemplate.syncSend(springTopic + ":tag0", "Hello, World! tag0!");
            System.out.printf("syncSend1 to topic %s sendResult=%s %n", springTopic, sendResult);
            // 发送自定义的对象,默认会转为JSON串进行发送
            sendResult = rocketMQTemplate.syncSend(userTopic, new User().setUserAge((byte) 18).setUserName("Kitty"));
            System.out.printf("syncSend3 to topic %s sendResult=%s %n", userTopic, sendResult);
            // 单方向发送消息
            rocketMQTemplate.sendOneWay(springTopic, "Hello, World! sendOneWay!");
            // 延迟消息。发送 spring message 对象, 指定超时时间是10 s, 并且指定延迟等级
            sendResult = rocketMQTemplate.syncSend(delayTopic, MessageBuilder.withPayload(
                    new User().setUserAge((byte) 21).setUserName("Delay")).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE).build(), 10 * 1000, 3);
            System.out.printf("syncSend5 to topic %s sendResult=%s %n", delayTopic, sendResult);
            // 异步发送, 指定回调与超时时间
            rocketMQTemplate.asyncSend(springTopic, new User().setUserAge((byte) 180).setUserName("asyncSend"), new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    System.out.println("asyncSend onSuccess:" + sendResult);
                }
    
                @Override
                public void onException(Throwable throwable) {
                    System.out.println("asyncSend error:" + throwable.getMessage());
                }
            }, 10 * 1000);
    
            /***********第二种发送方式*********/
            // convertAndSend 方法发送,其底层也是调用的syncSend 方法,只是没有返回结果
            String stringExtTopic = MQConstants.STRING_EXT_TOPIC;
            rocketMQTemplate.convertAndSend(stringExtTopic + ":tag0", "I'm from tag0");
            System.out.printf("syncSend topic %s tag %s %n", springTopic, "tag0");
            rocketMQTemplate.convertAndSend(stringExtTopic + ":tag1", "I'm from tag1");
            System.out.printf("syncSend topic %s tag %s %n", springTopic, "tag1");
        }
    }
    View Code

      生产者可以发送简单的字符串消息、顺序消息、异步消息、批量消息、同步消息等类型。

      另外也可以发送具有回复的消息,这种消息需要消费者实现RocketMQReplyListener 接口并且对消息进行回复。

      对于事务消息的发送是每个RocketTemplate 对应一个事务监听器,需要实现 RocketMQLocalTransactionListener, RocketMQLocalTransactionListener 可以加注解RocketMQTransactionListener 指明是事务消息的监听器。RocketMQLocalTransactionListener 本地事务监听器:

    package cn.qz.configuration;
    
    import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
    import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
    import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
    import org.apache.rocketmq.spring.support.RocketMQHeaders;
    import org.springframework.messaging.Message;
    
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @date 2022/4/19 21:04
     * @description 全局的事务监听器,一个MQ是一个。 rocketmqTempplate 所有的
     */
    // 可以用rocketMQTemplateBeanName 指定spring 容器中其他template 使用的事务监听器
    @RocketMQTransactionListener
    public class TransactionListenerImpl implements RocketMQLocalTransactionListener {
    
        private AtomicInteger transactionIndex = new AtomicInteger(0);
    
        private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<String, Integer>();
    
        @Override
        public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
            String transId = (String) msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID);
            System.out.printf("#### executeLocalTransaction is executed, msgTransactionId=%s %n",
                    transId);
            int value = transactionIndex.getAndIncrement();
            int status = value % 3;
            localTrans.put(transId, status);
            if (status == 0) {
                // Return local transaction with success(commit), in this case,
                // this message will not be checked in checkLocalTransaction()
                System.out.printf("    # COMMIT # Simulating msg %s related local transaction exec succeeded! ### %n", msg.getPayload());
                return RocketMQLocalTransactionState.COMMIT;
            }
    
            if (status == 1) {
                // Return local transaction with failure(rollback) , in this case,
                // this message will not be checked in checkLocalTransaction()
                System.out.printf("    # ROLLBACK # Simulating %s related local transaction exec failed! %n", msg.getPayload());
                return RocketMQLocalTransactionState.ROLLBACK;
            }
    
            System.out.printf("    # UNKNOW # Simulating %s related local transaction exec UNKNOWN! \n");
            return RocketMQLocalTransactionState.UNKNOWN;
        }
    
        @Override
        public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
            String transId = (String) msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID);
            RocketMQLocalTransactionState retState = RocketMQLocalTransactionState.COMMIT;
            Integer status = localTrans.get(transId);
            if (null != status) {
                switch (status) {
                    case 0:
                        retState = RocketMQLocalTransactionState.COMMIT;
                        break;
                    case 1:
                        retState = RocketMQLocalTransactionState.ROLLBACK;
                        break;
                    case 2:
                        retState = RocketMQLocalTransactionState.UNKNOWN;
                        break;
                }
            }
            System.out.printf("------ !!! checkLocalTransaction is executed once," +
                            " msgTransactionId=%s, TransactionState=%s status=%s %n",
                    transId, retState, status);
            return retState;
        }
    }

    3. 消费者

    关于消费者有几个接口:

      RocketMQPushConsumerLifecycleListener  - 可以指明消费的起始位置

      RocketMQReplyListener - 消费并且回复消息

      RocketMQListener - 单向的消费消息。

    (1) 简单的字符串消费者

    package cn.qz.consumer;
    
    import cn.qz.constants.MQConstants;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.rocketmq.spring.annotation.ConsumeMode;
    import org.apache.rocketmq.spring.annotation.MessageModel;
    import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
    import org.apache.rocketmq.spring.annotation.SelectorType;
    import org.apache.rocketmq.spring.core.RocketMQListener;
    import org.springframework.stereotype.Component;
    
    /**
     * @date 2022/4/19 17:30
     * @description RocketMQListener: 接收没回复
     */
    @Component
    @Slf4j
    @RocketMQMessageListener(
    //        nameServer = "192.168.13.101:9876", // 指定其他nameserver
            topic = MQConstants.STRING_TOPIC,
            selectorType = SelectorType.TAG, // 默认就是按TAG 过滤
            selectorExpression = "tag0||tag1",  // 默认是 *, 接收所有的TAG
            consumeMode = ConsumeMode.CONCURRENTLY, // 默认就是该值。ConsumeMode.ORDERLY和MessageModel.BROADCASTING不能一起设置
            messageModel = MessageModel.BROADCASTING, // 默认是集群模式
            consumerGroup = MQConstants.STRING_TOPIC + "-consumer2")
    public class StringConsumer2 implements RocketMQListener<String> {
    
        @Override
        public void onMessage(String message) {
            log.info("message: {}", message);
        }
    }

    (2) RocketMQReplyListener 消费且回复消息

    package cn.qz.consumer;
    
    import cn.qz.constants.MQConstants;
    import cn.qz.model.User;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
    import org.apache.rocketmq.spring.core.RocketMQReplyListener;
    import org.springframework.stereotype.Component;
    
    /**
     * @date 2022/4/20 12:45
     * @description
     */
    @Component
    @Slf4j
    @RocketMQMessageListener(
            topic = MQConstants.SPRING_RECEIVE_OBJ,
            consumerGroup = MQConstants.SPRING_RECEIVE_OBJ + "-consumer")
    public class ObjectConsumerWithReply implements RocketMQReplyListener<User, User> {
    
        @Override
        public User onMessage(User message) {
            log.info("message: {}", message);
            User replyUser = new User().setUserAge((byte) 111).setUserName("replyUsername");
            return replyUser;
        }
    }

    (3) RocketMQPushConsumerLifecycleListener 指定起始位置

    package cn.qz.consumer;
    
    import cn.qz.constants.MQConstants;
    import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
    import org.apache.rocketmq.common.UtilAll;
    import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
    import org.apache.rocketmq.common.message.MessageExt;
    import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
    import org.apache.rocketmq.spring.core.RocketMQListener;
    import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
    import org.springframework.stereotype.Component;
    
    /**
     * @date 2022/4/20 13:50
     * @description
     */
    @Component
    @RocketMQMessageListener(topic = MQConstants.STRING_EXT_TOPIC, selectorExpression = "tag0||tag1", consumerGroup = "${spring.application.name}-message-ext-consumer")
    public class MessageExtConsumer implements RocketMQListener<MessageExt>, RocketMQPushConsumerLifecycleListener {
    
        @Override
        public void onMessage(MessageExt message) {
            System.out.printf("------- MessageExtConsumer received message, msgId: %s, body:%s \n", message.getMsgId(), new String(message.getBody()));
        }
    
        @Override
        public void prepareStart(DefaultMQPushConsumer consumer) {
            // set consumer consume message from now
            consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP);
            consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis()));
        }
    }

    (4) 关于消息的应答

      我们之前的消费者是实现MessageListenerConcurrently 接口,然后返回ConsumeConcurrentlyStatus 。 上面这种方式实际抛出异常之后会自动进行消息重新消费。

    package cn.qz.consumer;
    
    import cn.qz.constants.MQConstants;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.rocketmq.common.message.MessageExt;
    import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
    import org.apache.rocketmq.spring.core.RocketMQListener;
    import org.springframework.stereotype.Component;
    
    /**
     * @date 2022/4/20 19:53
     * @description 测试手动应答消息,抛出异常就相当于是消息消费失败
     */
    @RocketMQMessageListener(topic = MQConstants.STRING_SELF_ACK, consumerGroup = MQConstants.STRING_SELF_ACK + "-consumer")
    @Component
    @Slf4j
    public class SelfAckConsumer implements RocketMQListener<MessageExt> {
    
        @Override
        public void onMessage(MessageExt message) {
            String tags = message.getTags();
            if ("tag0".equals(tags)) {
                // 发生异常,顶层会返回ConsumeConcurrentlyStatus.RECONSUME_LATER。 相当于会重新发送。
                throw new RuntimeException("tag0 reconsume");
            } else {
                log.info("tags: {}, message: {}", tags, new String(message.getBody()));
            }
        }
    }

    4. 关于数据源的扩展

      我们可以基于ExtRocketMQTemplateConfiguration 扩展出其他的rocketmq 数据源,相当于注入多个rocketTemplate, 只是nameServer 和 beanName 不同。

    (1) ExtRocketMQTemplate

    package cn.qz.ext;
    
    import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration;
    import org.apache.rocketmq.spring.core.RocketMQTemplate;
    
    /**
     * @date 2022/4/19 16:17
     * @description 相当于多数据源,指定其他的nameServer。 使用的使用直接注入该对象即可
     */
    @ExtRocketMQTemplateConfiguration(nameServer = "${demo.rocketmq.extNameServer}")
    public class ExtRocketMQTemplate extends RocketMQTemplate {
    }

    (2) 本地事务监听器 - 用于事务消息

    package cn.qz.ext;
    
    import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
    import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
    import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
    import org.springframework.messaging.Message;
    
    /**
     * @date 2022/4/19 21:36
     * @description 针对extRocketMQTemplate 使用的事务监听器,执行本地事务以及回查
     */
    @RocketMQTransactionListener(rocketMQTemplateBeanName = "extRocketMQTemplate")
    public class ExtTransactionListenerImpl implements RocketMQLocalTransactionListener {
    
        @Override
        public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
            System.out.printf("ExtTransactionListenerImpl executeLocalTransaction and return UNKNOWN. \n");
            return RocketMQLocalTransactionState.UNKNOWN;
        }
    
        @Override
        public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
            System.out.printf("ExtTransactionListenerImpl checkLocalTransaction and return COMMIT. \n");
            return RocketMQLocalTransactionState.COMMIT;
        }
    }

    (3) 使用

        /**
         * 如果使用其他数据源的rocketTemplate,需要指定名称
         */
        @Resource(name = "extRocketMQTemplate")
        private RocketMQTemplate extRocketMQTemplate;

    补充:关于消息的应答和回复消息的实现。

      org.apache.rocketmq.client.consumer.listener.MessageListener 接口下面衍生出的并发MessageListenerConcurrently 和顺序访问MessageListenerOrderly 的接口。两个接口的实现DefaultMessageListenerConcurrently 和 DefaultMessageListenerOrderly 内部消费消息的时候try...catch 异常进行了捕获,发生异常就返回 ConsumeConcurrentlyStatus.RECONSUME_LATER。
      RocketMQReplyListener 回复消息是在其实现的handleMessage 方法处理的。是收到消费者回复的消息之后转换为消息对象org.apache.rocketmq.common.message.Message ,然后进行发送。

    1. 继承关系如下:

    2. org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer.DefaultMessageListenerOrderly#consumeMessage 源码如下:

            @SuppressWarnings("unchecked")
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                for (MessageExt messageExt : msgs) {
                    log.debug("received msg: {}", messageExt);
                    try {
                        long now = System.currentTimeMillis();
                        handleMessage(messageExt);
                        long costTime = System.currentTimeMillis() - now;
                        log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime);
                    } catch (Exception e) {
                        log.warn("consume message failed. messageExt:{}", messageExt, e);
                        context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis);
                        return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
                    }
                }
    
                return ConsumeOrderlyStatus.SUCCESS;
            }
        }
    
        private void handleMessage(
            MessageExt messageExt) throws MQClientException, RemotingException, InterruptedException {
            if (rocketMQListener != null) {
                rocketMQListener.onMessage(doConvertMessage(messageExt));
            } else if (rocketMQReplyListener != null) {
                Object replyContent = rocketMQReplyListener.onMessage(doConvertMessage(messageExt));
                Message<?> message = MessageBuilder.withPayload(replyContent).build();
    
                org.apache.rocketmq.common.message.Message replyMessage = MessageUtil.createReplyMessage(messageExt, convertToBytes(message));
                consumer.getDefaultMQPushConsumerImpl().getmQClientFactory().getDefaultMQProducer().send(replyMessage, new SendCallback() {
                    @Override public void onSuccess(SendResult sendResult) {
                        if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                            log.error("Consumer replies message failed. SendStatus: {}", sendResult.getSendStatus());
                        } else {
                            log.info("Consumer replies message success.");
                        }
                    }
    
                    @Override public void onException(Throwable e) {
                        log.error("Consumer replies message failed. error: {}", e.getLocalizedMessage());
                    }
                });
            }
        }

    3. org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer.DefaultMessageListenerConcurrently#consumeMessage 源码如下

            @SuppressWarnings("unchecked")
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                for (MessageExt messageExt : msgs) {
                    log.debug("received msg: {}", messageExt);
                    try {
                        long now = System.currentTimeMillis();
                        handleMessage(messageExt);
                        long costTime = System.currentTimeMillis() - now;
                        log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime);
                    } catch (Exception e) {
                        log.warn("consume message failed. messageExt:{}", messageExt, e);
                        context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis);
                        return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
                    }
                }
    
                return ConsumeOrderlyStatus.SUCCESS;
            }
        }
    
        private void handleMessage(
            MessageExt messageExt) throws MQClientException, RemotingException, InterruptedException {
            if (rocketMQListener != null) {
                rocketMQListener.onMessage(doConvertMessage(messageExt));
            } else if (rocketMQReplyListener != null) {
                Object replyContent = rocketMQReplyListener.onMessage(doConvertMessage(messageExt));
                Message<?> message = MessageBuilder.withPayload(replyContent).build();
    
                org.apache.rocketmq.common.message.Message replyMessage = MessageUtil.createReplyMessage(messageExt, convertToBytes(message));
                consumer.getDefaultMQPushConsumerImpl().getmQClientFactory().getDefaultMQProducer().send(replyMessage, new SendCallback() {
                    @Override public void onSuccess(SendResult sendResult) {
                        if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                            log.error("Consumer replies message failed. SendStatus: {}", sendResult.getSendStatus());
                        } else {
                            log.info("Consumer replies message success.");
                        }
                    }
    
                    @Override public void onException(Throwable e) {
                        log.error("Consumer replies message failed. error: {}", e.getLocalizedMessage());
                    }
                });
            }
        }

      参考: https://github.com/apache/rocketmq-spring/tree/master/rocketmq-spring-boot-samples/

  • 相关阅读:
    BUAA2020个人博客作业小结
    BUAA2020软工热身作业小结
    个人博客作业----总结
    个人阅读作业7
    超链接按钮点击变色,原来的链接恢复原色
    setInterval和setTimeout的区别以及setInterval越来越快问题的解决方法
    自定义网站404页面
    jQuery实现的上下滚动公告栏详细讲解
    K先生的博客
    Bootstrap4响应式布局之栅格系统
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/16175864.html
Copyright © 2020-2023  润新知