Springboot整合activemq(二):收發具有優先級的隊列方法

在使用mq作爲中間件做異步消息推送時,可能會遇到一個場景,就是消息在消費後執行一系列的邏輯到一半,突然遇到異常或者是斷電等之類問題,這時消息在mq的隊列中已經出隊列,而正常邏輯沒有執行完就異常終止,這樣就可能會造成數據的缺失和數據的不完整,如何解決這個問題?其實挺簡單的,就是在消息進入消費者消費的同時做一個記錄,再在邏輯執行完成後再刪除這條記錄或者是改變這條記錄的狀態,同時,在項目初始化時或者是執行一個定時任務掃描這個記錄表,如果存在則產生一條相同的記錄發送到activemq中(相同的隊列)。這樣就能解決這個問題。然而,今天在開發中我又思考到一個問題,如圖:

假設消息1沒有消費完成,則消息1要重新進入隊列,此時有一條消息4跟消息1操作的是同一條數據,如果消息1再進入隊列,則4會在1之前消費掉,此時數據就會發生錯亂,如果要保證消息1在消息4之前消費,則就需要對重新入隊列的消息1進行一些操作來使得消息1優先消費。

activemq是提供對隊列設置爲具有優先級消息屬性的功能,那麼下面就要來實現具有優先級屬性的消息隊列:

第一步,進入activemq的conf文件夾下,修改activemq.xml配置文件,在<policyEntries>標籤下插入一個配置:

<policyEntry queue="queue1" strictOrderDispatch="true" useCache="false" queuePrefetch="1" prioritizedMessages="true" />

完成後保存,並重啓mq服務。這條配置就配置了對於queue1這個隊列內的消息是具有優先級的屬性的。

第二步,整合一個springboot+activemq的demo項目用於測試。

第三步,就是具體的代碼實現了,首先我們來關注下springboot整合activemq的源碼:

1.JmsMessagingTemplate這個類是實現發送消息的主要類,正常我們只需要在producer中注入這個類,調用其中發送消息的方法就能夠實現消息的發送。具體代碼如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Service;

import javax.jms.Destination;

@Service
public class TestProducer {

    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    public void sendMsg(Destination destination, String text){
        jmsMessagingTemplate.convertAndSend(destination,text);
    }

}

但是,此消息是不具有優先級的,接着往下看源碼。

           2.在JmsMessagingTemplate類中實際上是封裝了一個JmsTemplate這個類,實際上這個類纔是真正實現消息發送的類,它只是將它進一步封裝而已。

package org.springframework.jms.core;

import java.util.Map;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jms.InvalidDestinationException;
import org.springframework.jms.JmsException;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessagingMessageConverter;
import org.springframework.jms.support.destination.DestinationResolutionException;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.core.AbstractMessagingTemplate;
import org.springframework.messaging.core.MessagePostProcessor;
import org.springframework.util.Assert;

public class JmsMessagingTemplate extends AbstractMessagingTemplate<Destination> implements JmsMessageOperations, InitializingBean {
    @Nullable
    private JmsTemplate jmsTemplate;
    private MessageConverter jmsMessageConverter = new MessagingMessageConverter();
    private boolean converterSet;
    @Nullable
    private String defaultDestinationName;

    public JmsMessagingTemplate() {
    }

    public JmsMessagingTemplate(ConnectionFactory connectionFactory) {
        this.jmsTemplate = new JmsTemplate(connectionFactory);
    }

    public JmsMessagingTemplate(JmsTemplate jmsTemplate) {
        Assert.notNull(jmsTemplate, "JmsTemplate must not be null");
        this.jmsTemplate = jmsTemplate;
    }

    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        if (this.jmsTemplate != null) {
            this.jmsTemplate.setConnectionFactory(connectionFactory);
        } else {
            this.jmsTemplate = new JmsTemplate(connectionFactory);
        }

    }

    @Nullable
    public ConnectionFactory getConnectionFactory() {
        return this.jmsTemplate != null ? this.jmsTemplate.getConnectionFactory() : null;
    }

    public void setJmsTemplate(@Nullable JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    @Nullable
    public JmsTemplate getJmsTemplate() {
        return this.jmsTemplate;
    }

    public void setJmsMessageConverter(MessageConverter jmsMessageConverter) {
        Assert.notNull(jmsMessageConverter, "MessageConverter must not be null");
        this.jmsMessageConverter = jmsMessageConverter;
        this.converterSet = true;
    }

    public MessageConverter getJmsMessageConverter() {
        return this.jmsMessageConverter;
    }

    public void setDefaultDestinationName(@Nullable String defaultDestinationName) {
        this.defaultDestinationName = defaultDestinationName;
    }

    @Nullable
    public String getDefaultDestinationName() {
        return this.defaultDestinationName;
    }

    public void afterPropertiesSet() {
        Assert.notNull(this.jmsTemplate, "Property 'connectionFactory' or 'jmsTemplate' is required");
        if (!this.converterSet && this.jmsTemplate.getMessageConverter() != null) {
            ((MessagingMessageConverter)this.jmsMessageConverter).setPayloadConverter(this.jmsTemplate.getMessageConverter());
        }

    }

    private JmsTemplate obtainJmsTemplate() {
        Assert.state(this.jmsTemplate != null, "No JmsTemplate set");
        return this.jmsTemplate;
    }

    public void send(Message<?> message) {
        Destination defaultDestination = (Destination)this.getDefaultDestination();
        if (defaultDestination != null) {
            this.send(defaultDestination, message);
        } else {
            this.send(this.getRequiredDefaultDestinationName(), message);
        }

    }

    public void convertAndSend(Object payload) throws MessagingException {
        this.convertAndSend((Object)payload, (MessagePostProcessor)null);
    }

    public void convertAndSend(Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
        Destination defaultDestination = (Destination)this.getDefaultDestination();
        if (defaultDestination != null) {
            this.convertAndSend((Object)defaultDestination, payload, (MessagePostProcessor)postProcessor);
        } else {
            this.convertAndSend(this.getRequiredDefaultDestinationName(), payload, postProcessor);
        }

    }

    public void send(String destinationName, Message<?> message) throws MessagingException {
        this.doSend(destinationName, message);
    }

    public void convertAndSend(String destinationName, Object payload) throws MessagingException {
        this.convertAndSend(destinationName, payload, (Map)null);
    }

    public void convertAndSend(String destinationName, Object payload, @Nullable Map<String, Object> headers) throws MessagingException {
        this.convertAndSend(destinationName, payload, headers, (MessagePostProcessor)null);
    }

    public void convertAndSend(String destinationName, Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
        this.convertAndSend(destinationName, payload, (Map)null, postProcessor);
    }

    public void convertAndSend(String destinationName, Object payload, @Nullable Map<String, Object> headers, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
        Message<?> message = this.doConvert(payload, headers, postProcessor);
        this.send(destinationName, message);
    }

    @Nullable
    public Message<?> receive() {
        Destination defaultDestination = (Destination)this.getDefaultDestination();
        return defaultDestination != null ? this.receive(defaultDestination) : this.receive(this.getRequiredDefaultDestinationName());
    }

    @Nullable
    public <T> T receiveAndConvert(Class<T> targetClass) {
        Destination defaultDestination = (Destination)this.getDefaultDestination();
        return defaultDestination != null ? this.receiveAndConvert(defaultDestination, targetClass) : this.receiveAndConvert(this.getRequiredDefaultDestinationName(), targetClass);
    }

    @Nullable
    public Message<?> receive(String destinationName) throws MessagingException {
        return this.doReceive(destinationName);
    }

    @Nullable
    public <T> T receiveAndConvert(String destinationName, Class<T> targetClass) throws MessagingException {
        Message<?> message = this.doReceive(destinationName);
        return message != null ? this.doConvert(message, targetClass) : null;
    }

    @Nullable
    public Message<?> sendAndReceive(Message<?> requestMessage) {
        Destination defaultDestination = (Destination)this.getDefaultDestination();
        return defaultDestination != null ? this.sendAndReceive(defaultDestination, requestMessage) : this.sendAndReceive(this.getRequiredDefaultDestinationName(), requestMessage);
    }

    @Nullable
    public Message<?> sendAndReceive(String destinationName, Message<?> requestMessage) throws MessagingException {
        return this.doSendAndReceive(destinationName, requestMessage);
    }

    @Nullable
    public <T> T convertSendAndReceive(String destinationName, Object request, Class<T> targetClass) throws MessagingException {
        return this.convertSendAndReceive(destinationName, request, (Map)null, (Class)targetClass);
    }

    @Nullable
    public <T> T convertSendAndReceive(Object request, Class<T> targetClass) {
        return this.convertSendAndReceive((Object)request, (Class)targetClass, (MessagePostProcessor)null);
    }

    @Nullable
    public <T> T convertSendAndReceive(String destinationName, Object request, @Nullable Map<String, Object> headers, Class<T> targetClass) throws MessagingException {
        return this.convertSendAndReceive(destinationName, request, headers, targetClass, (MessagePostProcessor)null);
    }

    @Nullable
    public <T> T convertSendAndReceive(Object request, Class<T> targetClass, @Nullable MessagePostProcessor postProcessor) {
        Destination defaultDestination = (Destination)this.getDefaultDestination();
        return defaultDestination != null ? this.convertSendAndReceive((Object)defaultDestination, request, (Class)targetClass, (MessagePostProcessor)postProcessor) : this.convertSendAndReceive(this.getRequiredDefaultDestinationName(), request, targetClass, postProcessor);
    }

    @Nullable
    public <T> T convertSendAndReceive(String destinationName, Object request, Class<T> targetClass, @Nullable MessagePostProcessor requestPostProcessor) throws MessagingException {
        return this.convertSendAndReceive(destinationName, request, (Map)null, targetClass, requestPostProcessor);
    }

    @Nullable
    public <T> T convertSendAndReceive(String destinationName, Object request, @Nullable Map<String, Object> headers, Class<T> targetClass, @Nullable MessagePostProcessor postProcessor) {
        Message<?> requestMessage = this.doConvert(request, headers, postProcessor);
        Message<?> replyMessage = this.sendAndReceive(destinationName, requestMessage);
        return replyMessage != null ? this.getMessageConverter().fromMessage(replyMessage, targetClass) : null;
    }

    protected void doSend(Destination destination, Message<?> message) {
        try {
            this.obtainJmsTemplate().send(destination, this.createMessageCreator(message));
        } catch (JmsException var4) {
            throw this.convertJmsException(var4);
        }
    }

    protected void doSend(String destinationName, Message<?> message) {
        try {
            this.obtainJmsTemplate().send(destinationName, this.createMessageCreator(message));
        } catch (JmsException var4) {
            throw this.convertJmsException(var4);
        }
    }

    @Nullable
    protected Message<?> doReceive(Destination destination) {
        try {
            javax.jms.Message jmsMessage = this.obtainJmsTemplate().receive(destination);
            return this.convertJmsMessage(jmsMessage);
        } catch (JmsException var3) {
            throw this.convertJmsException(var3);
        }
    }

    @Nullable
    protected Message<?> doReceive(String destinationName) {
        try {
            javax.jms.Message jmsMessage = this.obtainJmsTemplate().receive(destinationName);
            return this.convertJmsMessage(jmsMessage);
        } catch (JmsException var3) {
            throw this.convertJmsException(var3);
        }
    }

    @Nullable
    protected Message<?> doSendAndReceive(Destination destination, Message<?> requestMessage) {
        try {
            javax.jms.Message jmsMessage = this.obtainJmsTemplate().sendAndReceive(destination, this.createMessageCreator(requestMessage));
            return this.convertJmsMessage(jmsMessage);
        } catch (JmsException var4) {
            throw this.convertJmsException(var4);
        }
    }

    @Nullable
    protected Message<?> doSendAndReceive(String destinationName, Message<?> requestMessage) {
        try {
            javax.jms.Message jmsMessage = this.obtainJmsTemplate().sendAndReceive(destinationName, this.createMessageCreator(requestMessage));
            return this.convertJmsMessage(jmsMessage);
        } catch (JmsException var4) {
            throw this.convertJmsException(var4);
        }
    }

    private JmsMessagingTemplate.MessagingMessageCreator createMessageCreator(Message<?> message) {
        return new JmsMessagingTemplate.MessagingMessageCreator(message, this.getJmsMessageConverter());
    }

    protected String getRequiredDefaultDestinationName() {
        String name = this.getDefaultDestinationName();
        if (name == null) {
            throw new IllegalStateException("No 'defaultDestination' or 'defaultDestinationName' specified. Check configuration of JmsMessagingTemplate.");
        } else {
            return name;
        }
    }

    @Nullable
    protected Message<?> convertJmsMessage(@Nullable javax.jms.Message message) {
        if (message == null) {
            return null;
        } else {
            try {
                return (Message)this.getJmsMessageConverter().fromMessage(message);
            } catch (Exception var3) {
                throw new MessageConversionException("Could not convert '" + message + "'", var3);
            }
        }
    }

    protected MessagingException convertJmsException(JmsException ex) {
        if (!(ex instanceof DestinationResolutionException) && !(ex instanceof InvalidDestinationException)) {
            return (MessagingException)(ex instanceof org.springframework.jms.support.converter.MessageConversionException ? new MessageConversionException(ex.getMessage(), ex) : new MessagingException(ex.getMessage(), ex));
        } else {
            return new org.springframework.messaging.core.DestinationResolutionException(ex.getMessage(), ex);
        }
    }

    private static class MessagingMessageCreator implements MessageCreator {
        private final Message<?> message;
        private final MessageConverter messageConverter;

        public MessagingMessageCreator(Message<?> message, MessageConverter messageConverter) {
            this.message = message;
            this.messageConverter = messageConverter;
        }

        public javax.jms.Message createMessage(Session session) throws JMSException {
            try {
                return this.messageConverter.toMessage(this.message, session);
            } catch (Exception var3) {
                throw new MessageConversionException("Could not convert '" + this.message + "'", var3);
            }
        }
    }
}

我們可以跟蹤方法的執行,進入到JmsTemplate這個類中,發現裏面336行封裝着一個方法。

protected void doSend(MessageProducer producer, Message message) throws JMSException {
        if (this.deliveryDelay >= 0L) {
            producer.setDeliveryDelay(this.deliveryDelay);
        }

        if (this.isExplicitQosEnabled()) {
            producer.send(message, this.getDeliveryMode(), this.getPriority(), this.getTimeToLive());
        } else {
            producer.send(message);
        }

    }

這裏就可以看到producer這個對象調用的send方法中有設置幾個參數:

(1)、message不用說了,就是要發送的消息內容;

(2)、Delivery Mode翻譯過來就是發送模式發送模式;

(3)、priority!這就是我們要找的優先級,傳入0-9整數,數字越大優先級越高;

(4)、timeToLive延時發送時間;

Ok,到這裏我們就可以看到,如果要執行這個方法就只需要isExplicitQosEnabled()這個方法返回值爲true。

再看代碼的第175行的方法:

public void setQosSettings(QosSettings settings) {
        Assert.notNull(settings, "Settings must not be null");
        this.setExplicitQosEnabled(true);
        this.setDeliveryMode(settings.getDeliveryMode());
        this.setPriority(settings.getPriority());
        this.setTimeToLive(settings.getTimeToLive());
    }

這個方法就是設置上面幾個參數的設置,只需傳入QosSettings這個對象就行。

來看JmsTemplate這個類的構造方法:

public JmsTemplate() {
        this.transactionalResourceFactory = new JmsTemplate.JmsTemplateResourceFactory();
        this.messageIdEnabled = true;
        this.messageTimestampEnabled = true;
        this.pubSubNoLocal = false;
        this.receiveTimeout = 0L;
        this.deliveryDelay = -1L;
        this.explicitQosEnabled = false;
        this.deliveryMode = 2;
        this.priority = 4;
        this.timeToLive = 0L;
        this.initDefaultStrategies();
    }

可以看到幾個參數的默認值,也就是沒有執行任何操作時如果將explicitQosEnabled設置爲true則該消息發送出去優先級默認爲4。

          3.所以我們將生產者代碼進行修改:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.QosSettings;
import org.springframework.stereotype.Service;

import javax.jms.DeliveryMode;
import javax.jms.Destination;

@Service
public class TestProducer {

    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    public void sendMsg(Destination destination, String text, int priority){
        //獲取jmsTemplate對象
        JmsTemplate jmsTemplate = jmsMessagingTemplate.getJmsTemplate();
        //創建QosSettings對象
        QosSettings settings = new QosSettings();
        //設置優先級
        settings.setPriority(priority);
        //設置發送模式
        settings.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
        //設置延時發送時間
        settings.setTimeToLive(1000L);
        //將設置傳入
        jmsTemplate.setQosSettings(settings);
        //發送消息
        jmsMessagingTemplate.convertAndSend(destination,text);
    }

}

這樣生產者就寫完了。

           4.測試

           首先,先寫一個消費者,監聽隊列queue1:

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class TestConsumer {

    @JmsListener(destination = "queue1")
    public void receiveTest(String text){
        System.out.println("接收到queue1發送的消息:"+text);
    }
}

再寫一個controller類可以通過前端調用發送消息的方法:

import com.example.springboot.mq.demo.producer.TestProducer;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.jms.Destination;
import java.util.Random;

@RestController
public class TestController {

    @Autowired
    private TestProducer producer;

    @RequestMapping("/test")
    public String test(){
        Destination destination = new ActiveMQQueue("queue1");
        Random random = new Random(10);
        for (int i=0;i < 100;i++){
            //隨機產生優先級數字
            int priority = random.nextInt(9);
            producer.sendMsg(destination,"消息No."+i+"優先級爲:"+priority,priority);
        }
        return "發送成功";
    }
}

然後啓動項目進行測試,測試結果:

發送消息優先級爲:0
發送消息優先級爲:6
發送消息優先級爲:6
發送消息優先級爲:0
發送消息優先級爲:4
發送消息優先級爲:1
發送消息優先級爲:1
發送消息優先級爲:4
發送消息優先級爲:3
發送消息優先級爲:1
發送消息優先級爲:1
發送消息優先級爲:2
發送消息優先級爲:6
發送消息優先級爲:6
發送消息優先級爲:3
發送消息優先級爲:3
發送消息優先級爲:1
發送消息優先級爲:1
發送消息優先級爲:4
發送消息優先級爲:7
發送消息優先級爲:8
發送消息優先級爲:7
發送消息優先級爲:6
發送消息優先級爲:8
發送消息優先級爲:5
發送消息優先級爲:1
發送消息優先級爲:5
發送消息優先級爲:5
發送消息優先級爲:8
發送消息優先級爲:8
發送消息優先級爲:2
發送消息優先級爲:8
發送消息優先級爲:6
發送消息優先級爲:8
發送消息優先級爲:7
發送消息優先級爲:1
發送消息優先級爲:7
發送消息優先級爲:1
發送消息優先級爲:0
發送消息優先級爲:4
發送消息優先級爲:8
發送消息優先級爲:1
發送消息優先級爲:8
發送消息優先級爲:0
發送消息優先級爲:7
發送消息優先級爲:5
發送消息優先級爲:6
發送消息優先級爲:0
發送消息優先級爲:2
發送消息優先級爲:4

接收到的消息:

消息內容:1優先級爲:6
消息內容:20優先級爲:8
消息內容:23優先級爲:8
消息內容:28優先級爲:8
消息內容:29優先級爲:8
消息內容:31優先級爲:8
消息內容:33優先級爲:8
消息內容:40優先級爲:8
消息內容:42優先級爲:8
消息內容:19優先級爲:7
消息內容:21優先級爲:7
消息內容:34優先級爲:7
消息內容:36優先級爲:7
消息內容:44優先級爲:7
消息內容:2優先級爲:6
消息內容:12優先級爲:6
消息內容:13優先級爲:6
消息內容:22優先級爲:6
消息內容:32優先級爲:6
消息內容:46優先級爲:6
消息內容:24優先級爲:5
消息內容:26優先級爲:5
消息內容:27優先級爲:5
消息內容:45優先級爲:5
消息內容:4優先級爲:4
消息內容:7優先級爲:4
消息內容:18優先級爲:4
消息內容:39優先級爲:4
消息內容:49優先級爲:4
消息內容:8優先級爲:3
消息內容:14優先級爲:3
消息內容:15優先級爲:3
消息內容:11優先級爲:2
消息內容:30優先級爲:2
消息內容:48優先級爲:2
消息內容:5優先級爲:1
消息內容:6優先級爲:1
消息內容:9優先級爲:1
消息內容:10優先級爲:1
消息內容:16優先級爲:1
消息內容:17優先級爲:1
消息內容:25優先級爲:1
消息內容:35優先級爲:1
消息內容:37優先級爲:1
消息內容:41優先級爲:1
消息內容:0優先級爲:0
消息內容:3優先級爲:0
消息內容:38優先級爲:0
消息內容:43優先級爲:0
消息內容:47優先級爲:0

結果顯示,除第一條外,其他消息的接收全部是按消息的優先級出隊列。所以大功告成,至於第一個消息爲什麼沒有按優先級出隊列後續我再研究一下。

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