Bootstrap

【RocketMQ】整合SpringBoot

引入

前不久教大家如何使用docker来搭建RocketMQ环境了,这次就来教搭建如何使用RocketMQ中间件

各大MQ对比及选型

比较常见的MQ有:ActiveMQ、RabbitMQ、RocketMQ、Kafka

RabbitMQActiveMQRocketMQKafka
公司/社区RabbitApache阿里(apache)Apache
开发语言ErlangJavaJavaScala&Java
协议支持AMQP,XMPP,SMTP,STOMPOpenWire,STOMP,REST,XMPP,AMQP自定义协议自定义协议
可用性一般
单机吞吐量一般 1万高 10W非常高 百万级
消息延迟微秒级毫秒级毫秒级毫秒以内
消息可靠性一般

追求可用性:Kafka、 RocketMQ 、RabbitMQ

追求可靠性:RabbitMQ、RocketMQ

追求吞吐能力:RocketMQ、Kafka

追求消息低延迟:RabbitMQ、Kafka

基于SpringBoot的使用

1.引入依赖
		<dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot</artifactId>
            <version>2.2.0</version>
        </dependency>
2.Yml文件配置

简单配置:

rocketmq:
  producer:
    group: erbadagang-producer-group
# RocketMQ name-server地址
  name-server: 127.0.0.1:9876

详细配置:

# rocketmq 配置项,对应 RocketMQProperties 配置类
rocketmq:
  name-server: 127.0.0.1:9876 # RocketMQ Namesrv
  # Producer 配置项
  producer:
    group: erbadagang-producer-group # 生产者分组
    send-message-timeout: 3000 # 发送消息超时时间,单位:毫秒。默认为 3000 。
    compress-message-body-threshold: 4096 # 消息压缩阀值,当消息体的大小超过该阀值后,进行消息压缩。默认为 4 * 1024B
    max-message-size: 4194304 # 消息体的最大允许大小。。默认为 4 * 1024 * 1024B
    retry-times-when-send-failed: 2 # 同步发送消息时,失败重试次数。默认为 2 次。
    retry-times-when-send-async-failed: 2 # 异步发送消息时,失败重试次数。默认为 2 次。
    retry-next-server: false # 发送消息给 Broker 时,如果发送失败,是否重试另外一台 Broker 。默认为 false
    access-key: # Access Key ,可阅读 https://github.com/apache/rocketmq/blob/master/docs/cn/acl/user_guide.md 文档
    secret-key: # Secret Key
    enable-msg-trace: true # 是否开启消息轨迹功能。默认为 true 开启。可阅读 https://github.com/apache/rocketmq/blob/master/docs/cn/msg_trace/user_guide.md 文档
    customized-trace-topic: RMQ_SYS_TRACE_TOPIC # 自定义消息轨迹的 Topic 。默认为 RMQ_SYS_TRACE_TOPIC 。
  # Consumer 配置项
  consumer:
    listeners: # 配置某个消费分组,是否监听指定 Topic 。结构为 Map<消费者分组, <Topic, Boolean>> 。默认情况下,不配置表示监听。
      erbadagang-consumer-group:
        topic1: false # 关闭 test-consumer-group 对 topic1 的监听消费
2.消息发送

先注入RocketMQTemplate ,通过rocketMQTemplate 调用里面的发送消息方法convertAndSend(接收者,消息)

@Component
public class CustomerInfoProducer {

	private static final Logger log = LogUtils.getLogger();

	private static final String MQ_TOPIC = "CUSTOMER_INFO_SYNC";

	/**
	* rocketMQ日志
	*/
	private static final Logger rocketMQLogger = LogUtils.getLogger(CustomerInfoConstant.MQ_LOG_NAME);

	@Resource
	private RocketMQTemplate rocketMQTemplate;

	/**
	* 客户资料同步
	* @param customerInfoRespVO
	*/
	public void sendMessageToSZ(CustomerInfoRespVO customerInfoRespVO){
		log.info("Customer info data synchronization");
		rocketMQLogger.info("producer send msg");
		rocketMQTemplate.convertAndSend(MQ_TOPIC ,customerInfoRespVO);
	}
}
2.1 发送同步消息

发送同步消息API

//发送普通同步消息-Object
syncSend(String destination, Object payload);

//发送普通同步消息-Message
syncSend(String destination, Message<?> message);

//发送批量普通同步消息
syncSend(String destination, Collection<T> messages);

//发送普通同步消息-Object,并设置发送超时时间
syncSend(String destination, Object payload, long timeout);

//发送普通同步消息-Message,并设置发送超时时间
syncSend(String destination, Message<?> message, long timeout);

//发送批量普通同步消息,并设置发送超时时间
syncSend(String destination, Collection<T> messages, long timeout);

//发送普通同步延迟消息,并设置超时,这个下文会演示
syncSend(String destination, Message<?> message, long timeout, int delayLevel);
@Component
public class CustomerInfoProducer {

	private static final Logger log = LogUtils.getLogger();

	private static final String MQ_TOPIC = "CUSTOMER_INFO_SYNC";

	/**
	* rocketMQ日志
	*/
	private static final Logger rocketMQLogger = LogUtils.getLogger(CustomerInfoConstant.MQ_LOG_NAME);

	@Resource
	private RocketMQTemplate rocketMQTemplate;

	/**
	* 客户资料同步
	* @param customerInfoRespVO
	*/
	public void sendMessageToSZ(CustomerInfoRespVO customerInfoRespVO){
		log.info("Customer info data synchronization");
		rocketMQLogger.info("producer send msg");
		//发送消息,并接受返回结果
		SendResult sendResult = rocketMQTemplate.syncSend(MQ_TOPIC ,customerInfoRespVO);
		
		//发送消息,并设置超时时间单位毫秒
		//SendResult sendResult = rocketMQTemplate.syncSend(MQ_TOPIC ,customerInfoRespVO,6000);
		
		//发送批量消息,并设置超时时间单位毫秒和延迟5秒钟发送
		//级别分别对应的时间为1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 
		//List<CustomerInfoRespVO> customerInfoRespVOList = new ArrayList<>();
		//SendResult sendResult = rocketMQTemplate.syncSend(MQ_TOPIC ,customerInfoRespVOList,6000,2);
		
		//获得消息状态
		SendStatus sendStatus = sendResult.getSendStatus();
		//获得消息id
		String msgId = sendResult.getMsgId();
		//获得所在队列信息
		MessageQueue messageQueue = sendResult.getMessageQueue();
	}
}
/**
 * 普通发送
 * @param topic     消息主题
 * @param msg       消息体
 * @param <T>       消息泛型
 */
public <T> void send(String topic, T msg) {
    rocketMQTemplate.convertAndSend(topic + ":tag1", msg);
    //rocketMQTemplate.send(topic + ":tag1", MessageBuilder.withPayload(msg).build()); // 等价于上面一行
}


/**
 * 发送带tag的消息,直接在topic后面加上":tag"
 *
 * @param topic     消息主题
 * @param tag       消息tag
 * @param msg       消息体
 * @param <T>       消息泛型
 * @return
 */
public <T> SendResult sendTagMsg(String topic, String tag, T msg) {
    topic = topic + ":" + tag;
    return rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(msg).build());
}


/**
 * 发送同步消息(阻塞当前线程,等待broker响应发送结果,这样不太容易丢失消息)
 * sendResult为返回的发送结果
 */
public <T> SendResult sendMsg(String topic, T msg) {
    Message<T> message = MessageBuilder.withPayload(msg).build();
    //带key的消息
    //MessageBuilder.withPayload(msg).setHeader("KEYS", "1")
    SendResult sendResult = rocketMQTemplate.syncSend(topic, message);
    log.info("【sendMsg】sendResult={}", JSON.toJSONString(sendResult));
    return sendResult;
}
2.2 发送同步消息,并指定对应的队列
@Component
public class CustomerInfoProducer {

	private static final Logger log = LogUtils.getLogger();

	private static final String MQ_TOPIC = "CUSTOMER_INFO_SYNC";

	private static final String HASH_KEY= "1"

	/**
	* rocketMQ日志
	*/
	private static final Logger rocketMQLogger = LogUtils.getLogger(CustomerInfoConstant.MQ_LOG_NAME);

	@Resource
	private RocketMQTemplate rocketMQTemplate;

	/**
	* 客户资料同步
	* @param customerInfoRespVO
	*/
	public void sendMessageToSZ(CustomerInfoRespVO customerInfoRespVO){
		log.info("Customer info data synchronization");
		rocketMQLogger.info("producer send msg");
		//其他功能与上面同步一样,syncSendOrderly没有批量
		rocketMQTemplate.syncSendOrderly(MQ_TOPIC ,customerInfoRespVO,HASH_KEY);
	}
}
2.3 发送异步消息

发送异步消息API

//发送普通异步消息-Object
asyncSend(String destination, Object payload, SendCallback sendCallback);

//发送普通异步消息-Message
asyncSend(String destination, Message<?> message, SendCallback sendCallback);

//发送普通异步消息-Object,并设置发送超时时间
asyncSend(String destination, Object payload, SendCallback sendCallback, long timeout);

//发送普通异步消息-Message,并设置发送超时时间
asyncSend(String destination, Message<?> message, SendCallback sendCallback, long timeout);

//发送普通异步延迟消息,并设置超时,这个下文会演示
asyncSend(String destination, Message<?> message, SendCallback sendCallback, long timeout,
        int delayLevel);
@Component
public class CustomerInfoProducer {

	private static final Logger log = LogUtils.getLogger();

	private static final String MQ_TOPIC = "CUSTOMER_INFO_SYNC";

	private static final String HASH_KEY= "1"

	/**
	* rocketMQ日志
	*/
	private static final Logger rocketMQLogger = LogUtils.getLogger(CustomerInfoConstant.MQ_LOG_NAME);

	@Resource
	private RocketMQTemplate rocketMQTemplate;

	/**
	* 客户资料同步
	* @param customerInfoRespVO
	*/
	public void sendMessageToSZ(CustomerInfoRespVO customerInfoRespVO){
		log.info("Customer info data synchronization");
		rocketMQLogger.info("producer send msg");
		//发送异步消息 MQ_TOPIC 主题 customerInfoRespVO 消息内容  CustomerInfoSendCallbackImpl 回调方法
		rocketMQTemplate.asyncSend(MQ_TOPIC ,customerInfoRespVO,new CustomerInfoSendCallbackImpl());
		//发送异步消息,并设置超时时间单位毫秒
		//rocketMQTemplate.asyncSend(MQ_TOPIC ,customerInfoRespVO,new CustomerInfoSendCallbackImpl(),6000);
	}
}

@Slf4j
public class CustomerInfoSendCallbackImpl implements SendCallback{

        @Override
        public void onSuccess(SendResult sendResult) {
            log.info("消息发送成功");
            //成功后的处理
        }

        @Override
        public void onException(Throwable e) {
            log.info("消息发送失败");
            //失败后的处理
        }
    }
/**
 * 发送异步消息
 * 发送异步消息(通过线程池执行发送到broker的消息任务,执行完后回调:在SendCallback中可处理相关成功失败时的逻辑)
 * (适合对响应时间敏感的业务场景)
 * @param topic     消息Topic
 * @param msg       消息实体
 *
 */
public <T> void asyncSend(String topic, T msg) {
    Message<T> message = MessageBuilder.withPayload(msg).build();
    //带key的消息
    //MessageBuilder.withPayload(msg).setHeader("KEYS", "1")
    asyncSend(topic, message, new SendCallback() {
        @Override
        public void onSuccess(SendResult sendResult) {
            log.info("topic:{}消息---发送MQ成功---", topic);
        }

        @Override
        public void onException(Throwable throwable) {
            log.error("topic:{}消息---发送MQ失败 ex:{}---", topic, throwable.getMessage());
        }
    });
}


/**
 * 发送异步消息
 * 发送异步消息(通过线程池执行发送到broker的消息任务,执行完后回调:在SendCallback中可处理相关成功失败时的逻辑)
 * (适合对响应时间敏感的业务场景)
 * @param topic        消息Topic
 * @param message      消息实体
 * @param sendCallback 回调函数
 */
public void asyncSend(String topic, Message<?> message, SendCallback sendCallback) {
    rocketMQTemplate.asyncSend(topic, message, sendCallback);
}


/**
 * 发送异步消息
 *
 * @param topic         消息Topic
 * @param message       消息实体
 * @param sendCallback  回调函数
 * @param timeout       超时时间
 */
public void asyncSend(String topic, Message<?> message, SendCallback sendCallback, long timeout) {
    rocketMQTemplate.asyncSend(topic, message, sendCallback, timeout);
}
2.4 发送异步消息,并指定队列
@Component
public class CustomerInfoProducer {

	private static final Logger log = LogUtils.getLogger();

	private static final String MQ_TOPIC = "CUSTOMER_INFO_SYNC";

	private static final String HASH_KEY= "1"

	/**
	* rocketMQ日志
	*/
	private static final Logger rocketMQLogger = LogUtils.getLogger(CustomerInfoConstant.MQ_LOG_NAME);

	@Resource
	private RocketMQTemplate rocketMQTemplate;

	/**
	* 客户资料同步
	* @param customerInfoRespVO
	*/
	public void sendMessageToSZ(CustomerInfoRespVO customerInfoRespVO){
		log.info("Customer info data synchronization");
		rocketMQLogger.info("producer send msg");
		//发送异步消息 MQ_TOPIC 主题 customerInfoRespVO 消息内容  CustomerInfoSendCallbackImpl 回调方法
		rocketMQTemplate.asyncSendOrderly(MQ_TOPIC ,customerInfoRespVO,HASH_KEY,new CustomerInfoSendCallbackImpl());
		//发送异步消息,并设置超时时间单位毫秒
		//rocketMQTemplate.asyncSendOrderly(MQ_TOPIC ,customerInfoRespVO,HASH_KEY,new CustomerInfoSendCallbackImpl(),6000);
	}
}

@Slf4j
public class CustomerInfoSendCallbackImpl implements SendCallback{

        @Override
        public void onSuccess(SendResult sendResult) {
            log.info("消息发送成功");
            //成功后的处理
        }

        @Override
        public void onException(Throwable e) {
            log.info("消息发送失败");
            //失败后的处理
        }
    }
2.5 发送单向消息
/**
 * 单向消息
 * 特点为只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答
 * 此方式发送消息的过程耗时非常短,一般在微秒级别
 * 应用场景:适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集
 * @param topic     消息主题
 * @param msg       消息体
 * @param <T>       消息泛型
 */
public <T> void sendOneWayMsg(String topic, T msg) {
    Message<T> message = MessageBuilder.withPayload(msg).build();
    rocketMQTemplate.sendOneWay(topic, message);
}
2.6 发送事物消息

事物消息消费者与普通消息消费者一样,不需要特殊处理

/**
 * 发送事务消息
 *
 * @param txProducerGroup   事务消息的生产者组名称
 * @param topic             事务消息主题
 * @param tag               事务消息tag
 * @param msg               事务消息体
 * @param arg               事务消息监听器回查参数
 * @param <T>               事务消息泛型
 */
public <T> void sendTransaction(String txProducerGroup, String topic, String tag, T msg, T arg){
    if(!StringUtils.isEmpty(tag)){
        topic = topic + ":" + tag;
    }
    String transactionId = UUID.randomUUID().toString();
    Message<T> message = MessageBuilder.withPayload(msg)
            //header也有大用处
            .setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
            .setHeader("share_id", msg.getId())
            .build();
    TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(txProducerGroup, topic, message, arg);
    if(result.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)
            && result.getSendStatus().equals(SendStatus.SEND_OK)){
        log.info("事物消息发送成功");
    }
    log.info("事物消息发送结果:{}", result);
}

定义本地事务处理类,实现RocketMQLocalTransactionListener接口,以及加上@RocketMQTransactionListener注解,这个类似方法的调用是异步的;

executeLocalTransaction方法,当我们处理完业务后,可以根据业务处理情况,返回事务执行状态,有bollback, commit or unknown三种,分别是回滚事务,提交事务和未知;根据事务消息执行流程,如果返回bollback,则直接丢弃消息;如果是返回commit,则消费消息;如果是unknow,则继续等待,然后调用checkLocalTransaction方法,最多重试15次,超过了默认丢弃此消息;

checkLocalTransaction方法,是当MQ Server未得到MQ发送方应答,或者超时的情况,或者应答是unknown的情况,调用此方法进行检查确认,返回值和上面的方法一样;

@Slf4j
@Component
@RocketMQTransactionListener(txProducerGroup = "CUSTOMER_NOTICE_CONSUMER_GROUP")
public class TransactionListenerImpl implements RocketMQLocalTransactionListener {

    @Autowired
    private ShareService shareService;

    @Autowired
    private RocketmqTransactionLogMapper rocketmqTransactionLogMapper;

    /**
     * 发送prepare消息成功此方法被回调,该方法用于执行本地事务
     * @param message   回传的消息,利用transactionId即可获取到该消息的唯一Id
     * @param arg       调用send方法时传递的参数,当send时候若有额外的参数可以传递到send方法中,这里能获取到
     * @return          返回事务状态,COMMIT:提交  ROLLBACK:回滚  UNKNOW:回调
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object arg) {
        MessageHeaders headers = message.getHeaders();
        String transactionId = (String)headers.get(RocketMQHeaders.TRANSACTION_ID);
        Integer shareId = Integer.parseInt((String)headers.get("share_id"));
        try {
            shareService.auditBYIdWithRocketMqLog(shareId,(ShareAuditDTO)auditDTO,transactionId);

            //本地事物成功,执行commit
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            log.error("本地事物执行异常,e={}",e);
            //本地事物失败,执行rollback
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }

    //mq回调检查本地事务执行情况
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
        MessageHeaders headers = message.getHeaders();
        String transactionId = (String)headers.get(RocketMQHeaders.TRANSACTION_ID);

        RocketmqTransactionLog rocketmqTransactionLog = rocketmqTransactionLogMapper.selectOne(RocketmqTransactionLog
                .builder().transactionId(transactionId).build());
        if(rocketmqTransactionLog == null){
            log.error("如果本地事物日志没有记录,transactionId={}",transactionId);
            //本地事物失败,执行rollback
            return RocketMQLocalTransactionState.ROLLBACK;
        }
        //如果本地事物日志有记录,执行commit
        return RocketMQLocalTransactionState.COMMIT;
    }
}

留意上面的:发送Tag标签消息,没有单独抽出来说

主题创建

在这里插入图片描述

3.消息模式

一个消费者只能绑定一个队列,一个队列可以绑定多个消费者
如果需要按业务过滤对应的消息可以设置selectorExpression为对应的业务Tag消息

@RocketMQMessageListener注解参数说明:

  • consumerGroup:消费者订阅组,它是必需的,并且必须是唯一的
  • topic:主题名字,生产发送的主题名
  • consumeMode:消费模式,可选择并发或有序接收消息;默认CONCURRENTLY同时接收异步传递的消息
  • messageModel:消息模式,默认CLUSTERING集群消费;如果希望所有订阅者都接收消息,可以设置广播BROADCASTING
  • consumeThreadMax:消费者最大线程数,默认64
  • consumeTimeout:消息阻塞最长时间,默认15分钟
  • nameServer:服务器地址,默认读取配置文件地址,可以单独为消费者设置指定位置
  • selectorExpression:消费指定的Tag标签的业务消息
  • 更多查看官方解释
3.1 集群消息模式

一个项目多台服务器部署属于集群方式(默认也是集群)

public class CustomerInfoConstant {

    //MQ使用字段
    public static final String MQ_TOPIC= "CUSTOMER_INFO_SYNC";

    public static final String MQ_CONSUMER_GROUP = "CUSTOMER_NOTICE_CONSUMER_GROUP";
    
	public static final String MQ_CONSUMER_GROUP_TWO = "CUSTOMER_INFO_NOTICE_CONSUMER_GROUP";
}

@Component
@Service
@RocketMQMessageListener(topic = CustomerInfoConstant.MQ_TOPIC, consumerGroup = CustomerInfoConstant.MQ_CONSUMER_GROUP, consumeThreadMax = 10)
@RefreshScope
public class CustomerInfoConsumer implements RocketMQListener<CustomerInfoRespVO> {

    private static final Logger log = LogUtils.getLogger();

    @Resource
    CustomerInfoSyncService customerInfoSyncService;
    
    @Override
    public void onMessage(CustomerInfoRespVO customerInfoRespVO) {
        log.info("Trigger incremental synchronization of customer data. :{}", customerInfoRespVO);
        //消费逻辑
    }
}
3.2 广播消息模式

注意下面是两个服务,同一个消费者组,我们需要在后面加上messageModel = MessageModel.BROADCASTING来表示这个消费者组属于广播消费模式;
注意:广播模式下不分消费者组只要监听了同一个Topic就能同时消费一条消息

@Component
@Service
@RocketMQMessageListener(topic = CustomerInfoConstant.MQ_TOPIC, consumerGroup = CustomerInfoConstant.MQ_CONSUMER_GROUP, consumeThreadMax = 10, messageModel = MessageModel.BROADCASTING)
@RefreshScope
public class CustomerInfoConsumer implements RocketMQListener<CustomerInfoRespVO> {

    private static final Logger log = LogUtils.getLogger();

    @Resource
    CustomerInfoSyncService customerInfoSyncService;
    
    @Override
    public void onMessage(CustomerInfoRespVO customerInfoRespVO) {
        log.info("Trigger incremental synchronization of customer data. :{}", customerInfoRespVO);
        //消费逻辑
    }
}

@Component
@Service
@RocketMQMessageListener(topic = CustomerInfoConstant.MQ_TOPIC_, consumerGroup = CustomerInfoConstant.MQ_CONSUMER_GROUP_TWO, consumeThreadMax = 10, messageModel = MessageModel.BROADCASTING)
@RefreshScope
public class CustomerInfoConsumer implements RocketMQListener<CustomerInfoRespVO> {

    private static final Logger log = LogUtils.getLogger();

    @Resource
    CustomerInfoSyncService customerInfoSyncService;
    
    @Override
    public void onMessage(CustomerInfoRespVO customerInfoRespVO) {
        log.info("Trigger incremental synchronization of customer data. :{}", customerInfoRespVO);
        //消费逻辑
    }
}
消费者组创建

在这里插入图片描述

4.消费模式

消息分为有序消息和并发消息:

  • CONCURRENTLY 并发消息
  • ORDERLY 有序消息

MessageListenerOrderly正确消费返回ConsumeOrderlyStatus.SUCCESS,

稍后消费返回ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT

MessageListenerConcurrently正确消费返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS

稍后消费返回ConsumeConcurrentlyStatus.RECONSUME_LATER

顾名思义,有序消费模式是按照消息的顺序进行消费,但是除此之外,我发现和并发消费模式还有很大的区别的。

并发比有序消费快得多。

另一个区别是消费失败时的处理不同,有序消费模式返回ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT后,消费者会立马消费这条消息,而使用并发消费模式,返回ConsumeConcurrentlyStatus.RECONSUME_LATER后,要过好几秒甚至十几秒才会再次消费。我是在只有一条消息的情况下测试的。更重要的区别是,

返回ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT并不会增加消息的消费次数,mq消息有个默认最大消费次数16,消费次数到了以后,这条消息会进入死信队列,这个最大消费次数是可以在mqadmin中设置的。

mqadmin updateSubGroup -n 127.0.0.1:9876 -c DefaultCluster -g MonitorCumsumerGroupName -r 3

我测试后发现,并发模式下返回ConsumeConcurrentlyStatus.RECONSUME_LATER,同一个消息到达最大消费次数之后就不会再出现了。这说明有序消费模式可能并没有这个机制,这意味着你再有序消费模式下抛出固定异常,那么这条异常信息将会被永远消费,并且很可能会影响之后正常的消息。

有序消费模式,消费失败后,会马上拉这条信息。
并发消费模式则不会无限消费,而且消费失败后不会马上再消费。

结论是有序消费模式MessageListenerOrderly要慎重地处理异常,我则是用全局变量记录消息的错误消费次数,只要消费次数达到一定次数,那么就直接返回ConsumeOrderlyStatus.SUCCESS

小知识点:

  1. 发消息是使用Tag,Tag:用于区分过滤同一主题下的不同业务类型的消息,非常实用
  2. RocketMQ自带JSON转换,所以我们发消息的时候可以直接将对象传进去,避免null值的属性不传输过去。
;