ActiveMQ 與 Spring

xiangdong發表於2017-05-17

1. ActiveMQ安裝

1.1 下載(版本5.14.5)

點我官網下載

1.2 安裝

解壓下載的壓縮檔案到任意目錄中(eg. C:\Program Files (x86)\apache-activemq-5.14.5),進入%ACTIVEMQ_HOME%/bin目錄,根據自己的系統位數,進入32/64目錄,點選activemq.bat啟動ActiveMQ;

2. ActiveMQ與Spring整合使用

2.1 在Maven中新增ActiveMQ和JMS相關的pom,如下:
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>4.2.5.RELEASE</version>
        <!--<version>{spring.version}</version>-->
        </dependency>

        <!-- xbean 如<amq:connectionFactory /> -->
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.16</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-core</artifactId>
            <version>5.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-pool</artifactId>
            <version>5.12.1</version>
        </dependency>複製程式碼
2.2 新增配置檔案spring-activemq.xml

在配置檔案中加入以下配置資訊,每個配置資訊都有具體的解釋:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
          http://activemq.apache.org/schema/core 
          http://activemq.apache.org/schema/core/activemq-core.xsd">

    <!--配置連線ActiveMQ的連線基本資訊 -->
    <amq:connectionFactory id="amqConnectionFactory"
        brokerURL="tcp://localhost:61616" userName="admin" password="admin" />

    <!-- 配置JMS連線工廠 -->
    <bean id="connectionFactory"
        class="org.springframework.jms.connection.CachingConnectionFactory">
        <constructor-arg ref="amqConnectionFactory" />
        <property name="sessionCacheSize" value="100" />
    </bean>

    <!-- 定義訊息佇列(Queue) -->
    <bean id="demoQueueDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <!-- 設定訊息佇列的名字 -->
        <constructor-arg>
            <value>testQueue</value>
        </constructor-arg>
    </bean>

    <!-- 配置JMS模板(Queue),Spring提供的JMS工具類,它傳送、接收訊息。 -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultDestination" ref="demoQueueDestination" />
        <property name="receiveTimeout" value="10000" />
        <!-- true是topic,false是queue,預設是false -->
        <property name="pubSubDomain" value="false" />
    </bean>

    <!-- 配置訊息佇列監聽者(Queue) -->
    <!-- 開啟監聽器,會立即去消費訊息(即,起到實時消費通訊的作用) -->
    <!-- <bean id="queueMessageListener" class="com.hp.common.listener.QueueMessageListener"></bean> -->

    <!-- 顯示注入訊息監聽容器(Queue),配置連線工廠,監聽的目標是demoQueueDestination,監聽器是上面定義的監聽器 -->
    <!-- <bean id="queueListenerContainer"
        class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destination" ref="demoQueueDestination" />
        <property name="messageListener" ref="queueMessageListener" />
    </bean>
 -->
</beans>複製程式碼

注:在配置檔案中,一定不要忘記加入ActiveMQ和JMS相關的schema

2.3 建立Producer和Consumer相關的Service

建立ProducerService,用於傳送資訊到訊息中心

@Service
public class ProducerService {

    @Resource(name = "jmsTemplate")
    private JmsTemplate jmsTemplate;

    private Queue queue;

    /**
     * 根據目的地傳送訊息
     */
    public void sendMessage(Destination destination, final String msg) {
        System.out.println(Thread.currentThread().getName() + " 向佇列" + destination.toString()
                + "傳送訊息------->" + msg);
        jmsTemplate.send(destination, new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                return session.createTextMessage(msg);
            }
        });
    }
    public String send(String userId, String msg) {
        System.out.println(
                Thread.currentThread().getName() + " 向 " + userId + " 的佇列" + userId.toString() + "傳送訊息------>" + msg);
        queue = new ActiveMQQueue(userId);
        jmsTemplate.send(queue, new MessageCreator() {

            @Override
            public Message createMessage(Session session) throws JMSException {
                TextMessage message=session.createTextMessage(msg);
                message.setStringProperty(userId, msg);
                return message;
            }
        });
        return "傳送成功";

    }

    /**
     * 向預設目的地傳送訊息
     */
    public String sendMessage(final String msg) {
        String destination = jmsTemplate.getDefaultDestinationName();
        System.out
                .println(Thread.currentThread().getName() + " 向佇列" + destination + "傳送訊息---------------------->" + msg);
        jmsTemplate.send(new MessageCreator() {
            public Message createMessage(Session session) throws JMSException {
                return session.createTextMessage(msg);
            }
        });
        return "傳送成功";
    }

}複製程式碼

建立ConsumerService,用於接受訊息


@Service
public class ConsumerService{

    @Resource(name = "jmsTemplate")
    private JmsTemplate jmsTemplate;

    public String receive(Destination destination) {
        TextMessage textMessage = (TextMessage) jmsTemplate.receive(destination);
        try {
            System.out.println("從佇列" + destination.toString() + "收到了訊息:\t" + textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
        return textMessage.toString();
    }

    public String receive(String userId) {
        Queue queue=new ActiveMQQueue(userId+"?consumer.prefetchSize=4");
        Message message = null;
        String property=null;

        try {

             message=jmsTemplate.receive(queue);
             property=message.getStringProperty(userId);
            System.out.println("從佇列" + queue.toString() + "收到了訊息:\t" + property);
        } catch (JMSException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        return property;
    }

}複製程式碼
2.4 新增Controller,用於曝露介面
@Controller
@RequestMapping(value="/mq")
public class MessageController {
    private Logger logger = Logger.getLogger(MessageController.class);

    @Resource(name = "demoQueueDestination")
    private Destination destination;

    @Autowired
    private ProducerService producer;

    @Autowired
    private ConsumerService consumer;


    @RequestMapping(value = "/SendMessage", method = RequestMethod.POST,produces="application/json")
    @ResponseBody
    public void send(@RequestParam(value = "userId",required=false)String userId,@RequestParam(value = "msg")String msg) {
        logger.info(Thread.currentThread().getName() + "------------send to jms Start");
        if (userId==null||"".equals(userId)) {
            producer.sendMessage(destination, msg);
        }else {
            producer.send(userId, msg);
        }

        logger.info(Thread.currentThread().getName() + "------------send to jms End");
    }

    @RequestMapping(value = "/ReceiveMessage", method = RequestMethod.GET)
    @ResponseBody
    public Object receive(@RequestParam(value = "userId",required=false)String userId) {
        logger.info(Thread.currentThread().getName() + "------------receive from jms Start");
        String tm=null;
        if (userId==null||"".equals(userId)) {
             tm = consumer.receive(destination);
        } else {
             tm = consumer.receive(userId);
        }
        logger.info(Thread.currentThread().getName() + "------------receive from jms End");
        return tm.toString();
    }

}複製程式碼
2.5 配置監聽器(ek)

如果在配置檔案中開啟了監聽器的註釋,即開啟監聽器,消費者會立即去消費訊息,則還需要新增如下程式碼:

public class QueueMessageListener implements MessageListener{

    @Override
    public void onMessage(Message message) {
        TextMessage tm=(TextMessage) message;
        try {
            System.out.println("QueueMessageListener監聽到了文字訊息:\t"
                    + tm.getText());
            //do other work
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}複製程式碼

3. 測試

啟動tomcat,將Javaweb專案執行在tomcat中,通過postman測試介面和方法
接受訊息介面:http://localhost:8080/`{project_neme}`/mq/ReceiveMessage?userId={訊息佇列名稱}
傳送訊息介面:http://localhost:8080/`{project_neme}`/mq/SendMessage?userId={訊息佇列名稱}&msg={引數}

4. 其他場景及技術應用

場景1: 對於mq佇列中的訊息,系統需要做一些監控或者問題的跟蹤,則需要去檢視MQ中的資料,但是有需要保證在檢視之後不會被刪除,因為在P2P模式中,consumer.receive()後訊息之後,訊息就被消費,MQ不會傳送其他consumer,對於這種場景該如何考慮採用ActiveMQ的何種技術去做?
場景2:將使用JDBC持久化的ActiveMQ轉換為其他儲存方式(檔案儲存、Kaha、memory),需要做資料遷移,那如何實現?
解決:對於這兩種場景,都可以用訊息佇列中訊息檢視的方式去實現;
第一個場景,使用ActiveMQ的Browser可以檢視未被消費的資訊,這樣既保證資料不會被消費,也可以實現自己的其他業務;
第二個場景,可以使用Browser將未被消費的資訊拿出來,然後再通過produce.send()的方式,將訊息傳送到其他儲存方式的ActiveMQ上;

以下程式碼實現了使用Browser讀出某個佇列中未消費的所有訊息,並將它們放到list中

public class BrowersService {

    private static final Logger logger=LogManager.getLogger(BrowersService.class);
    //配置檔案配置的jmsTemplate
    @Resource(name = "jmsTemplate")
    private JmsTemplate jmsTemplate;

    public void getMessageFromQuese(String queueName){
        List<String> message=jmsTemplate.browse(queueName, new BrowserCallback<List<String>>() {
            @Override
            public List<String> doInJms(Session session, QueueBrowser browser) throws JMSException {
                Enumeration<TextMessage> enumeration=browser.getEnumeration();
                List<String> messages=new ArrayList<>();
                while (enumeration.hasMoreElements()) {
                    TextMessage textMessage = (TextMessage) enumeration.nextElement();
                    logger.info("Message text: "+ textMessage.getText()
                    +" ID: "+textMessage.getJMSMessageID());
                    messages.add(textMessage.getText());
                }
                return messages;
            }
        });
        logger.info("message from browser  "+message);
    }
}複製程式碼

相關文章