SpringBoot整合RocketMQ消息隊列demo

前言

在此只引入關鍵依賴,關於SpringBoot等其他基礎依賴請自行準備。
另外,本文中的實現方式爲點對點的方式。

 

開始

首先,在pom文件中引入依賴

    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>4.2.0</version>
    </dependency>

①一個Producer

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Slf4j
@Component
public class MessageProducer {

    @Value(value = "${rocketMQ.address}")
    private String address;

    private DefaultMQProducer producer;

    private static final Integer RETRY_TIMES = 5;


    /**
     * 消息生產者初始化
     */
    @PostConstruct
    private void init(){
        try {
            producer = new DefaultMQProducer("zxy");
            producer.setNamesrvAddr(address);
            producer.setInstanceName("MessageProducer");
            producer.setRetryTimesWhenSendAsyncFailed(RETRY_TIMES);
            producer.start();
        } catch (Exception e){
            log.error("消息生產者初始化失敗");
        }
    }

    /**
     * 消息生產者關閉
     */
    @PreDestroy
    public void destroy(){
        try {
            producer.shutdown();
        } catch (Exception e){
            log.error("消息生產者關閉失敗");
        }
    }


    /**
     * 異步發送mq
     * @param topic
     * @param tags
     * @param body
     */
    @Async
    public void sendMessage(String topic, String tags, String body) {
        try {
            Message message = new Message(topic, tags, body.getBytes("UTF-8"));
            producer.send(message, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    log.info("消息發送成功, sendResult:{}" , sendResult);
                }

                @Override
                public void onException(Throwable throwable) {
                    Integer retryTimes = producer.getRetryTimesWhenSendAsyncFailed();
                    log.info("消息發送失敗, retryTimes:{}" , retryTimes);
                }
            });

        } catch (Exception e) {
            log.error("消息發送失敗,topic:{}, tags:{}, body:{}", topic, tags, body);
        }
    }
}

②一個Consumer

import com.zxy.devops.project.biz.enums.MessageTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;


@Slf4j
@Component
public class DefaultConsumer {

    @Value(value = "${rocketMQ.address}")
    private String address;

    @Autowired
    private DefaultListener defaultListener;

    @PostConstruct
    public void messageSubscribe() throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DefaultConsumer");
        consumer.setNamesrvAddr(address);
        consumer.setInstanceName("DefaultConsumer");
        consumer.subscribe("default-topic", "*");
        consumer.registerMessageListener(defaultListener);
        consumer.start();
        log.info("Default consumer start.");
    }
}

③一個Listener

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.stereotype.Component;

import java.util.List;


@Slf4j
@Component
public class DefaultListener implements MessageListenerConcurrently {

    private static final Integer RETRY_TIMES = 5;

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
        log.info(Thread.currentThread().getName()
                +" Receive New Messages: " + list.size());

        Message message = list.get(0);

        if (message == null) {
            log.error("處理消息時, message 爲空");
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }

        log.info("處理消息 , topic={}, tags={}, keys={}, reConsumeTimes={}",
                message.getTopic(),
                message.getTags(),
                message.getKeys(),
                ((MessageExt) message).getReconsumeTimes());


        String defaultTag = "tag";
        try {
            //處理mq
            if (defaultTag.equals(message.getTags())) {

                //獲取消息body
                String body = new String(message.getBody());
                
                //TODO 執行業務邏輯
            }

            log.info("處理MQ消息消費 成功 , topic={}, tag={}, key={}, reConsumeTimes={}",
                    message.getTopic(),
                    message.getTags(),
                    message.getKeys(),
                    ((MessageExt) message).getReconsumeTimes());
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;


        } catch (Exception e) {

            log.error("處理MQ消息消費 保存記錄失敗,topic={}, tag={}, key={}, reConsumeTimes={}, body={},e={}",
                    message.getTopic(), message.getTags(), message.getKeys(), ((MessageExt) message).getReconsumeTimes(), new String(message.getBody()), e);
            if (((MessageExt) message).getReconsumeTimes() < RETRY_TIMES) {
                log.info("處理MQ,準備重新消費消息");
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            } else {
                // 超過5次,就不重發了
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }

        }
    }
}

 

總結

本文中實現了SpringBoot整合RocketMQ點對點的方式,其中Producer可通用向自定義topic生產消息,Consumer可自定義訂閱topic,如ConsumerA訂閱topic-A,ConsumerB訂閱topic-B。在本文中重試次數到達上限後,可另行添加補償機制,如生成日誌存入數據庫,後續批量處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章