• 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/

  • 相关阅读:
    使用 asp.net mvc和 jQuery UI 控件包
    ServiceStack.Redis 使用教程
    HTC T8878刷机手册
    Entity Framework CodeFirst 文章汇集
    2011年Mono发展历程
    日志管理实用程序LogExpert
    使用 NuGet 管理项目库
    WCF 4.0路由服务Routing Service
    精进不休 .NET 4.0 (1) asp.net 4.0 新特性之web.config的改进, ViewStateMode, ClientIDMode, EnablePersistedSelection, 控件的其它一些改进
    精进不休 .NET 4.0 (7) ADO.NET Entity Framework 4.0 新特性
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/16175864.html
Copyright © 2020-2023  润新知