【SpringBoot筆記17】使用JMS操作ActiveMQ

ActiveMQ版本:5.15.11

1 JMS原始API操作ActiveMQ

1.1 queue點對點模式

ActiveMqConfig.java

package com.tao.springbootdemo.mq.activemq;

import org.apache.activemq.ActiveMQConnection;

/**
 * activemq的配置
 */
public class ActiveMqConfig {

    public static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
    public static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
    public static final String BROKER_URL = "tcp://192.168.48.128:61616";
}

QueueProducer.java生產者:

package com.tao.springbootdemo.mq.activemq.jms.queue;

import com.tao.springbootdemo.mq.activemq.ActiveMqConfig;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * queue消息生產者
 */
public class QueueProducer {
    private static final Logger LOG = LoggerFactory.getLogger(QueueProducer.class);

    private Connection connection;
    private Session session;
    private MessageProducer producer;

    public QueueProducer(String queueName) {
        init(queueName);
    }

    /**
     * 初始化
     */
    public void init(String queueName) {
        try {
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMqConfig.BROKER_URL);
            connection = connectionFactory.createConnection();
            connection.start();
            session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            Queue queue = session.createQueue(queueName);
            producer = session.createProducer(queue);
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }

    /**
     * 發送消息
     */
    public void sendMsg(String message) {
        try {
            ActiveMQTextMessage msg = new ActiveMQTextMessage();
            msg.setText(message);
            producer.send(msg);
            session.commit();
            LOG.info(Thread.currentThread().getName() + " send a message: {}", msg.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

QueueConsumer.java消費者:

package com.tao.springbootdemo.mq.activemq.jms.queue;

import com.tao.springbootdemo.mq.activemq.ActiveMqConfig;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * queue消息消費者
 */
public class QueueConsumer {

    private static final Logger LOG = LoggerFactory.getLogger(QueueConsumer.class);

    private Connection connection;
    private Session session;
    private MessageConsumer consumer;

    public QueueConsumer(String queueName) {
        init(queueName);
    }

    /**
     * 初始化
     */
    public void init(String queueName) {
        try {
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMqConfig.BROKER_URL);
            connection = connectionFactory.createConnection();
            connection.start();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Queue queue = session.createQueue(queueName);
            consumer = session.createConsumer(queue);
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }

    /**
     * 接收消息
     */
    public void receiveMsg() {
        try {
            consumer.setMessageListener(new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    try {
                        TextMessage textMessage = (TextMessage) message;
                        LOG.info(Thread.currentThread().getName() + " receive a message : {}", textMessage.getText());
                    } catch (JMSException e) {
                        LOG.error("" + e);
                    }
                }
            });
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

ActiveMqQueueExample.java測試類:

package com.tao.springbootdemo.mq.activemq.jms.queue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * queue消息測試類
 */
public class ActiveMqQueueExample {

    private static final Logger LOG = LoggerFactory.getLogger(ActiveMqQueueExample.class);
    public static final String QUEUE_NAME = "activemq-queue-example";

    public static void main(String[] args) {
        /**
         * 循環發送消息
         */
        new Thread(() -> {
            QueueProducer queueProducer = new QueueProducer(QUEUE_NAME);
            while (true) {
                queueProducer.sendMsg("發送一條queue消息");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    LOG.error("" + e);
                }
            }
        }).start();

        /**
         * 監聽消息
         */
        new Thread(() -> {
            QueueConsumer queueConsumer = new QueueConsumer(QUEUE_NAME);
            queueConsumer.receiveMsg();
        }).start();
    }
}

程序運行結果:
在這裏插入圖片描述

1.2 topic發佈訂閱模式

TopicPublisher.java發佈者:

package com.tao.springbootdemo.mq.activemq.jms.topic;

import com.tao.springbootdemo.mq.activemq.ActiveMqConfig;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * topic消息發佈者
 */
public class TopicPublisher {
    private static final Logger LOG = LoggerFactory.getLogger(TopicPublisher.class);

    private Connection connection;
    private Session session;
    private MessageProducer producer;

    public TopicPublisher(String topicName) {
        init(topicName);
    }

    /**
     * 初始化
     */
    public void init(String topicName) {
        try {
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMqConfig.BROKER_URL);
            connection = connectionFactory.createConnection();
            connection.start();
            session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            Topic topic = session.createTopic(topicName);
            producer = session.createProducer(topic);
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }

    /**
     * 發送消息
     */
    public void sendMsg(String message) {
        try {
            ActiveMQTextMessage msg = new ActiveMQTextMessage();
            msg.setText(message);
            producer.send(msg);
            session.commit();
            LOG.info(Thread.currentThread().getName() + " send a message: {}", msg.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

TopicSubscriber.java訂閱者:

package com.tao.springbootdemo.mq.activemq.jms.topic;

import com.tao.springbootdemo.mq.activemq.ActiveMqConfig;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.*;

/**
 * topic消息訂閱者
 *
 */
public class TopicSubscriber {
    private static final Logger LOG = LoggerFactory.getLogger(TopicSubscriber.class);

    private Connection connection;
    private Session session;
    private MessageConsumer consumer;

    public TopicSubscriber(String topicName) {
        init(topicName);
    }

    /**
     * 初始化
     */
    public void init(String topicName) {
        try {
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMqConfig.BROKER_URL);
            connection = connectionFactory.createConnection();
            connection.start();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            Topic topic = session.createTopic(topicName);
            consumer = session.createConsumer(topic);
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }

    /**
     * 接收消息
     */
    public void receiveMsg() {
        try {
            consumer.setMessageListener(new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    try {
                        TextMessage textMessage = (TextMessage) message;
                        LOG.info(Thread.currentThread().getName() + " receive a message : {}", textMessage.getText());
                    } catch (JMSException e) {
                        LOG.error("" + e);
                    }
                }
            });
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

ActiveMqTopicExample.java測試類:

package com.tao.springbootdemo.mq.activemq.jms.topic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * topic消息測試類
 */
public class ActiveMqTopicExample {
    private static final Logger LOG = LoggerFactory.getLogger(ActiveMqTopicExample.class);
    public static final String TOPIC_NAME = "activemq-topic-test";

    public static void main(String[] args) {

        /**
         * 循環發送消息
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                TopicPublisher topicPublisher = new TopicPublisher(TOPIC_NAME);
                while (true) {
                    topicPublisher.sendMsg("發送一條topic消息");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        LOG.error("" + e);
                    }
                }
            }
        }).start();

        /**
         * 接收消息
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    TopicSubscriber topicSubscriber = new TopicSubscriber(TOPIC_NAME);
                    topicSubscriber.receiveMsg();
                }
            }
        }).start();
    }
}

程序運行結果:
在這裏插入圖片描述

2 SpringBoot通過JmsTemplate操作ActiveMQ

Spring Boot版本2.2.3

引入依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
	<groupId>org.messaginghub</groupId>
	<artifactId>pooled-jms</artifactId>
</dependency>

官網提示:

如果想要使用連接池,可以添加一個org.messaginghub:pooled-jms依賴,並配置JmsPoolConnectionFactory的屬性:

spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50

配置連接屬性:

# activemq配置
spring.activemq.broker-url: tcp://192.168.48.128:61616
spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50

# 自定義的queue和topic名字
custom.spring.activemq.queue.name=custom-activemq-queue
custom.spring.activemq.topic.name=custom-activemq-topic

自定義JmsConfig配置類,配置@EnableJms,開啓對JMS的支持,並創建一個queue和一個topic交由Spring管理:

package com.tao.springbootdemo.config;

import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;

import javax.jms.Queue;
import javax.jms.Topic;

@Configuration
@EnableJms
public class JmsConfig {

    @Value("${custom.spring.activemq.queue.name}")
    private String queueName;
    @Value("${custom.spring.activemq.topic.name}")
    private String topicName;

    @Bean
    public Queue queue() {
        return new ActiveMQQueue(queueName);
    }

    @Bean
    public Topic topic() {
        return new ActiveMQTopic(topicName);
    }
}

2.1 queue點對點模式

QueueProducer.java消息生產者:

package com.tao.springbootdemo.mq.activemq.springjms.queue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.jms.Queue;

/**
 * queue消息生產者
 */
@RestController
@RequestMapping("/queue")
public class QueueProducer {

    @Autowired
    private JmsTemplate jmsTemplate;
    @Autowired
    private Queue queue;

    /**
     * 發送消息
     *
     * @param msg
     * @return
     */
    @GetMapping("/send/{msg}")
    public String sendQueueMsg(@PathVariable("msg") String msg) {
        jmsTemplate.convertAndSend(queue, msg);
        return "OK";
    }
}

QueueConsumer.java消息消費者:

package com.tao.springbootdemo.mq.activemq.springjms.queue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.TextMessage;

/**
 * queue消息消費者
 */
@Component
public class QueueConsumer {

    private static final Logger LOG = LoggerFactory.getLogger(QueueConsumer.class);

    /**
     * 監聽並消費消息
     *
     * @param msg
     */
    @JmsListener(destination = "${custom.spring.activemq.queue.name}")
    public void receiveQueueMsg(Object msg) {
        try {
            TextMessage textMessage = (TextMessage) msg;
            LOG.info(Thread.currentThread().getName() + " receive a message: {}", textMessage.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

測試結果:

通過web請求觸發消息發送:
在這裏插入圖片描述

消費者接收到消息:
在這裏插入圖片描述

2.2 topic發佈訂閱模式

要想使用topic,需要添加一個屬性配置:

# 是否開啓pub/sub模式,默認是false
spring.jms.pub-sub-domain=true

注意:配置spring.jms.pub-sub-domain屬性只能一次支持一種,要想同時支持queuetopic,需要重寫JmsListenerContainerFactory

TopicPublisher.java消息發佈者:

package com.tao.springbootdemo.mq.activemq.springjms.topic;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.jms.Topic;

/**
 * topic消息發佈者
 */
@RestController
@RequestMapping("/topic")
public class TopicPublisher {

    @Autowired
    private Topic topic;
    @Autowired
    private JmsTemplate jmsTemplate;

    /**
     * 發佈消息
     *
     * @param msg
     * @return
     */
    @GetMapping("/publish/{msg}")
    public String publishTopicMsg(@PathVariable("msg") String msg) {
        jmsTemplate.convertAndSend(topic, msg);
        return "OK";
    }
}

TopicSubscriber1.java消息訂閱者1:

package com.tao.springbootdemo.mq.activemq.springjms.topic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.TextMessage;

/**
 * topic消息訂閱者1
 */
@Component
public class TopicSubscriber1 {

    private static final Logger LOG = LoggerFactory.getLogger(TopicSubscriber1.class);

    /**
     * 監聽消息
     *
     * @param msg
     */
    @JmsListener(destination = "${custom.spring.activemq.topic.name}")
    public void receiveMsg(Object msg) {
        try {
            TextMessage textMessage = (TextMessage) msg;
            LOG.info("TopicSubscriber1 receive a message: {}", textMessage.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

TopicSubscriber2.java消息訂閱者2:

package com.tao.springbootdemo.mq.activemq.springjms.topic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.TextMessage;

/**
 * topic消息訂閱者2
 */
@Component
public class TopicSubscriber2 {

    private static final Logger LOG = LoggerFactory.getLogger(TopicSubscriber2.class);

    /**
     * 監聽消息
     *
     * @param msg
     */
    @JmsListener(destination = "${custom.spring.activemq.topic.name}")
    public void receiveMsg(Object msg) {
        try {
            TextMessage textMessage = (TextMessage) msg;
            LOG.info("TopicSubscriber2 receive a message: {}", textMessage.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

測試結果:

通過web請求觸發消息發佈:
在這裏插入圖片描述

兩個消息訂閱者收到消息:
在這裏插入圖片描述

2.3 自定義containerFactory解決queuetopic不能同時使用的問題

首先在JmsConfig中自定義兩個beanqueueListenerFactory用於queue模式,topicListenerFactory用於topic模式:

JmsConfig.java

package com.tao.springbootdemo.config;

import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;

import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.Topic;

@Configuration
@EnableJms
public class JmsConfig {

    @Value("${custom.spring.activemq.queue.name}")
    private String queueName;
    @Value("${custom.spring.activemq.topic.name}")
    private String topicName;

    @Bean
    public Queue queue() {
        return new ActiveMQQueue(queueName);
    }

    @Bean
    public Topic topic() {
        return new ActiveMQTopic(topicName);
    }

    @Bean
    public JmsListenerContainerFactory<?> queueListenerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        // 設置queue模式
        factory.setPubSubDomain(false);
        factory.setConnectionFactory(connectionFactory);
        return factory;
    }

    @Bean
    public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        // 設置topic模式
        factory.setPubSubDomain(true);
        factory.setConnectionFactory(connectionFactory);
        return factory;
    }
}

然後在@JmsListener註解上添加屬性containerFactory選擇對應的bean

QueueConsumer.java

package com.tao.springbootdemo.mq.activemq.springjms.queue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.TextMessage;

/**
 * queue消息消費者
 */
@Component
public class QueueConsumer {

    private static final Logger LOG = LoggerFactory.getLogger(QueueConsumer.class);

    /**
     * 監聽並消費消息
     *
     * @param msg
     */
    @JmsListener(destination = "${custom.spring.activemq.queue.name}", containerFactory = "queueListenerFactory")
    public void receiveQueueMsg(Object msg) {
        try {
            TextMessage textMessage = (TextMessage) msg;
            LOG.info(Thread.currentThread().getName() + " receive a message: {}", textMessage.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

TopicSubscriber1.java

package com.tao.springbootdemo.mq.activemq.springjms.topic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.TextMessage;

/**
 * topic消息訂閱者1
 */
@Component
public class TopicSubscriber1 {

    private static final Logger LOG = LoggerFactory.getLogger(TopicSubscriber1.class);

    /**
     * 監聽消息
     *
     * @param msg
     */
    @JmsListener(destination = "${custom.spring.activemq.topic.name}", containerFactory = "topicListenerFactory")
    public void receiveMsg(Object msg) {
        try {
            TextMessage textMessage = (TextMessage) msg;
            LOG.info("TopicSubscriber1 receive a message: {}", textMessage.getText());
        } catch (JMSException e) {
            LOG.error("" + e);
        }
    }
}

測試程序結果:
在這裏插入圖片描述

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