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/

posted @ 2022-04-21 20:42  QiaoZhi  阅读(4939)  评论(0编辑  收藏  举报