ActiveMQ---知識點整理

一.背景介紹

1.1 java消息服務:

 不同系統之間的信息交換,是我們開發中比較常見的場景,比如系統A要把數據發送給系統B,這個問題我們應該如何去處理? 1999年,原來的SUN公司領銜提出了一種面向消息的中間件服務--JMS規範(標準);常用的幾種信息交互技術(httpClient、hessian、dubbo、jms、webservice 五種).

1.2JMS概述:

 JMS即Java消息服務(Java Message Service的簡稱),是Java EE 的標準/規範之一。這種規範(標準)指出:消息的發送應該是異步的、非阻塞的。也就是說消息的發送者發送完消息後就直接返回了,不需要等待接收者返回後才能返回,發送者和接收者可以說是互不影響。所以這種規範(標準)能夠減輕或消除系統瓶頸,實現系統之間去除耦合,提高系統的整體可伸縮性和靈活性。JMS只是Java EE中定義的一組標準API,它自身並不是一個消息服務系統,它是消息傳送服務的一個抽象,也就是說它定義了消息傳送的接口而並沒有具體實現。

1.3ActiveMQ概述:

 我們知道JMS只是消息服務的一組規範和接口,並沒有具體的實現,而ActiveMQ就是JMS規範的具體實現;它是Apache下的一個項目,採用Java語言開發;是一款非常流行的開源消息服務器.

1.4 ActiveMQ與JMS關係:

 我們知道,JMS只是定義了一組有關消息傳送的規範和標準,並沒有真正實現,也就說JMS只是定義了一組接口而已;就像JDBC抽象了關係數據庫訪問、JPA抽象了對象與關係數據庫映射、JNDI抽象了命名目錄服務訪問一樣,JMS具體的實現由不同的消息中間件廠商提供,比如Apache ActiveMQ就是JMS規範的具體實現,Apache ActiveMQ纔是一個消息服務系統,而JMS不是。

                       

二.ActiveMQ的使用

2.1 ActiveMQ環境搭建:

  •  ActiveMQ運行需要Java的支持,首先需要配置Java環境變量;
  • 切換到解壓後的activemq的bin目錄下 cd /usr/local/apache-activemq-5.15.2 去啓動
  • 切換到bin目錄下,啓動:./activemq start ;關閉:./activemq stop
  • 啓動後有兩個端口號,一個是web控制檯:8161,一個是消息服務broker連接端口:61616
  • web管理控制檯admin URL地址:http://localhost:8161  默認登錄賬號 admin  密碼 admin,注意:Linux防火前要關閉 ;通過這個地址可以即時訪問交互信息.如下圖:

  • 消息服務broker URL地址 : tcp://localhost:61616
2.2 Java消息隊列JMS整體設計結構

2.2.1 基本要素:1、生產者producer ; 2、消費者consumer ; 3、消息服務broker

2.2.1 交互模型:

2.2.3 JMS兩種消息傳送模式

  • 點對點( Point-to-Point):專門用於使用隊列Queue傳送消息;基於隊列Queue的點對點消息只能被一個消費者消費,如多個消費者都註冊到同一個消息隊列上,當生產者發送一條消息後,而只有其中一個消費者會接收到該消息,而不是所有消費者都能接收到該消息。
  •  發佈/訂閱(Publish/Subscribe):專門用於使用主題Topic傳送消息。基於主題的發佈與訂閱消息能被多個消費者消費,生產者發送的消息,所有訂閱了該topic的消費者都能接收到。

2.3 Java消息隊列JMS API總體概覽:

 2.3.1JMS API 概覽

  •  JMS API可以分爲3個主要部分:
   1、公共API:可用於向一個隊列或主題發送消息或從其中接收消息;
   2、點對點API:專門用於使用隊列Queue傳送消息;
   3、發佈/訂閱API:專門用於使用主題Topic傳送消息。
  •  JMS公共API:
  在JMS公共API內部,和發送與接收消息有關的JMS API接口主要是:ConnectionFactory/Connection/Session/Message/Destination/MessageProducer/MessageConsumer . 它們的關係是:一旦有了ConnectionFactory,就可以創建Connection,一旦有了Connection,就可以創建Session,而一旦有了Session,就可以創建Message、MessageProducer和MessageConsumer。

  • JMS點對點API:

點對點(p2p)消息傳送模型API是指JMS API之內基於隊列(Queue)的接口:QueueConnectionFactory/QueueConnection/QueueSession/Message/Queue/QueueSender/QueueReceiver. 從接口的命名可以看出,大多數接口名稱僅僅是在公共API接口

稱之前添加Queue一詞。一般來說,使用點對點消息傳送模型的應用程序將使用基於隊列的API,而不使用公共API 。

  •  JMS發佈/訂閱API:

  發佈/訂閱消息傳送模型API是指JMS API之內基於主題(Topic)的接口:TopicConnectionFactory/TopicConnection/

TopicSession/Message/Topic/TopicPublisher/TopicSubscriber. 由於基於主題(Topic)的JMS API類似於基於隊列(Queue)

的API,因此在大多數情況下,Queue這個詞會由Topic取代。


2.4 ActiveMQ點對點發送與接收消息示例


 2.4.1 簡單示例:

 寫一個採用Queue隊列方式點對點發送接收文本信息的Demo,先寫發送者,如下:

package com.kinglong.activemq.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息發送者
 *
 */
public class Sender {

    /**消息服務器的連接地址**/
    public static final String BROKER_URL = "tcp://192.168.174.129:61616";

    public static void main(String[] args) {
        Sender sender = new Sender();
        sender.sendMessage("Hello ActiveMQ.");
    }

    /**
     * 發送消息
     *
     * @param msg
     */
    public void sendMessage (String msg) {

        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;

        try {
            //1.創建一個連接工廠
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);

            //2.創建一個連接
            connection = connectionFactory.createConnection();

            //3.創建一個Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

            //4.創建消息,此處創建了一個文本消息
            Message message = session.createTextMessage(msg);

            //5.創建一個目的地
            Destination destination = session.createQueue("myQueue");

            //6.創建一個消息的生產者(發送者)
            messageProducer = session.createProducer(destination);

            //7.發送消息
            messageProducer.send(message);

        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                //關閉連接釋放資源
                if (null != messageProducer) {
                    messageProducer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}
 再寫接收者:

package com.kinglong.activemq.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class Receiver {

    /**消息服務器的連接地址**/
    public static final String BROKER_URL = "tcp://192.168.174.129:61616";

    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        receiver.receiveMessage();
    }

    /**
     * 接收消息
     *
     */
    public void receiveMessage () {

        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;

        try {
            //1.創建一個連接工廠
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);

            //2.創建一個連接
            connection = connectionFactory.createConnection();

            //3.創建一個Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

            //4.創建一個目的地
            Destination destination = session.createQueue("myQueue");

            //5.創建一個消息的消費者(接收者)
            messageConsumer = session.createConsumer(destination);

            //接收消息之前,需要把連接啓動一下
            connection.start();

            //6.接收消息
            Message message = messageConsumer.receive();

            //判斷消息的類型
            if (message instanceof TextMessage) { //判斷是否是文本消息
                String text = ((TextMessage) message).getText();
                System.out.println("接收到的消息內容是:" + text);
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                //關閉連接釋放資源
                if (null != messageConsumer) {
                    messageConsumer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}
總結:

 創建過程中有幾個重要的注意點,說明一下:

1.  session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);  
   // 其中:Boolean.FALSE表示本次會話不開啓事務管理,假如需要開啓事務管理,將其改爲Boolean.TRUE即可
   //同時需要在發送消息後添加session.commit(),否則,消息是不會被提交的.
   //Session.AUTO_ACKNOWLEDGE表示消息確認機制
	AUTO_ACKNOWLEDGE:自動確認
	CLIENT_ACKNOWLEDGE:客戶端確認
	SESSION_TRANSACTED:事務確認,如果使用事務推薦使用該確認機制
	AUTO_ACKNOWLEDGE:懶散式確認,消息偶爾不會被確認,也就是消息可能會被重複發送.但發生的概率很小
2.  connection.start();
   //在消息接收端,接受消息前需要加入這段代碼,開啓連接,否則一樣無法獲取消息.
3.  Destination destination = session.createQueue("myQueue");
	//創建目的地時,如果做測試收不到信息,可以將目的地名稱修改一下,我用的是IDEA,不清楚爲何,
	//有時候收不到信息,修改一下就好了,猜測可能是緩存的原因吧

發佈與訂閱的topic方式實際與點對點的queue方式,代碼通用很多,只是在創建目的地Destination時候創建爲

Destination destination = session.createTopic("myTopic");
2.4.2Queue與Topic比較

2.4.3拉模式與推模式

  a.點對點消息,如果沒有消費者在監聽隊列,消息將保留在隊列中,直至消息消費者連接到隊列爲止。這種消息傳遞模型是
傳統意義上的懶模型或輪詢模型。在此模型中,消息不是自動推動給消息消費者的,而是要由消息消費者從隊列中請求獲得(拉模式)。 
 b.pub/sub消息傳遞模型基本上是一個推模型。在該模型中,消息會自動廣播,消息消費者無須通過主動請求或輪詢主題的方法來獲得新的消息。

2.5 ActiveMQ消息類型
  • 1、TextMessage 文本消息:攜帶一個java.lang.String作爲有效數據(負載)的消息,可用於字符串類型的信息交換;
  • 2、ObjectMessage 對象消息:攜帶一個可以序列化的Java對象作爲有效負載的消息,可用於Java對象類型的信息交換;
  • 3、MapMessage 映射消息:攜帶一組鍵值對的數據作爲有效負載的消息,有效數據值必須是Java原始數據類型(或者它們的包裝類)及String。即:byte, short, int, long, float, double, char, boolean, String
  • 4、BytesMessage 字節消息 :攜帶一組原始數據類型的字節流作爲有效負載的消息;
  • 5、StreamMessage 流消息:攜帶一個原始數據類型流作爲有效負載的消息,它保持了寫入流時的數據類型,寫入什麼類型,
  • 則讀取也需要是相同的類型;

需要注意的是:如果使用對象消息做Demo時,如果使用之前的連接創建方式可能會無法接收到消息,因爲安全問題.所以,如果想要使用Demo測試對象消息,創建連接時建議改爲這樣(官網推薦的連接方式):

//創建對象消息連接工廠
           ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
           List<String > list = new ArrayList<String>();
           list.add("com.kinglong.activemq.receiver");
           list.add("com.kinglong.activemq.model");
           activeMQConnectionFactory.setTrustedPackages(list);
2.6 ActiveMQ事務消息和非事務消息

 消息分爲事務消息和非事務消息

1、事務消息:創建會話Session使用transacted=true
connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
2、非事務消息:創建會話Session使用transacted=false
 connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

        事務消息必須在發送和接收完消息後顯式地調用session.commit();

 事務性消息,不管設置何種消息確認模式,都會自動被確認;與設置的確認機制無關,但官方推薦事務性消息使用事務確認機制.

2.7 ActiveMQ消息確認機制

 消息只有在被確認之後,才認爲已經被成功消費,然後消息纔會從隊列或主題中刪除。消息的成功消費通常包含三個階段:

                           

  (1)、客戶接收消息;(2)、客戶處理消息;(3)、消息被確認;

確認機制前面提過一下,共有四種:

  • (1)、Session.AUTO_ACKNOWLEDGE;客戶(消費者)成功從receive方法返回時,或者從MessageListener.onMessage方法成功返回時,會話自動確認消息,然後自動刪除消息.
  • (2)、Session.CLIENT_ACKNOWLEDGE;客戶通過顯式調用消息的acknowledge方法確認消息,。 即在接收端調用message.acknowledge();方法,否則,消息是不會被刪除的.
  • (3)、Session. DUPS_OK_ACKNOWLEDGE ;不是必須確認,是一種“懶散的”消息確認,消息可能會重複發送,在第二次重新傳送消息時,消息頭的JMSRedelivered會被置爲true標識當前消息已經傳送過一次,客戶端需要進行消息的重複處理控制。
  • (4)、 Session.SESSION_TRANSACTED;事務提交併確認。
2.8 ActiveMQ持久化消息與非持久化消息

 messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);//不持久化

 messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);//持久化的,當然activemq發送消息默認都是持久化的

說明:

設置完後,如果爲持久化,那麼消息在沒有被消費前都會被寫入本地磁盤kahadb文件中保存起來,即使服務器宕機,也不會影響

消息.如果是非持久化的,那麼,服務一旦宕機之類的情況發生,消息即會被刪除.

ActiveMQ默認是持久化的.

2.9 ActiveMQ消息過濾

ActiveMQ提供了一種機制,可根據消息選擇器中的標準來執行消息過濾,只接收符合過濾標準的消息;

生產者可在消息中放入特有的標誌,而消費者使用基於這些特定的標誌來接收消息;

1、發送消息放入特殊標誌:message.setStringProperty(name, value);

2、接收消息使用基於特殊標誌的消息選擇器:

MessageConsumer createConsumer(Destination destination, String messageSelector);

注:消息選擇器是一個字符串,語法與數據庫的SQL相似,相當於SQL語句where條件後面的內容;

具體代碼如下:

發送端代碼:

package com.bjpowernode.activemq.selector;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * 消息發送者
 *
 */
public class Sender {

    /**消息服務器的連接地址**/
    public static final String BROKER_URL = "tcp://192.168.174.129:61616";

    public static void main(String[] args) {
        Sender sender = new Sender();
        sender.sendMessage("Hello ActiveMQ.");
    }

    /**
     * 發送消息
     *
     * @param msg
     */
    public void sendMessage (String msg) {

        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;

        try {
            //1.創建一個連接工廠
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);

            //2.創建一個連接
            connection = connectionFactory.createConnection();

            //3.創建一個Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);



            //5.創建一個目的地
            Destination destination = session.createQueue("myQueue");

            //6.創建一個消息的生產者(發送者)
            messageProducer = session.createProducer(destination);

            //設置發送的消息是否需要持久化
            messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);//這裏使用不持久化
			
			//創建一個循環,測試消息標識的使用
            for (int i=0; i<20; i++) {
                //4.創建消息,此處創建了一個文本消息
                Message message = session.createTextMessage(msg+i);

                //將消息設置一個特有的標識
                message.setIntProperty("id", i);

                //7.發送消息
                messageProducer.send(message);
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                //關閉連接釋放資源
                if (null != messageProducer) {
                    messageProducer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}
接收端代碼:

package com.kinglong.activemq.selector;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class Receiver {

    /**消息服務器的連接地址**/
    public static final String BROKER_URL = "tcp://192.168.174.129:61616";

    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        receiver.receiveMessage();
    }

    /**
     * 接收消息
     *
     */
    public void receiveMessage () {

        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;

        try {
            //1.創建一個連接工廠
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);

            //2.創建一個連接
            connection = connectionFactory.createConnection();

            //3.創建一個Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

            //4.創建一個目的地
            Destination destination = session.createQueue("myQueue");

            //5.創建一個消息的消費者(接收者),selector即爲消息選擇器,通過選擇需要的標識,過濾消息接受id爲10-15之  //間的消息
            String selector = "id >=10 and id<=15";
            messageConsumer = session.createConsumer(destination, selector);

            //接收消息之前,需要把連接啓動一下
            connection.start();

            while (true) {
                //6.接收消息  同步接收
                Message message = messageConsumer.receive();

                //判斷消息的類型
                if (message instanceof TextMessage) { //判斷是否是文本消息
                    String text = ((TextMessage) message).getText();
                    System.out.println("接收到的消息內容是:" + text);
                }
            }
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            try {
                //關閉連接釋放資源
                if (null != messageConsumer) {
                    messageConsumer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}
2.10 ActiveMQ消息接收方式

  同步接收:receive()方法接收消息叫同步接收,就是之前的Demo代碼使用的接收方式.在不使用循環方法時接收端代碼執行

一次即結束.

 異步接收:使用監聽器接收消息,這種接收方式叫異步接收,接收端會一直處於監聽狀態,只要有消息產生,即會接收消息.

下面是異步接收代碼:

package com.kinglong.activemq.listener;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class Receiver {

    /**消息服務器的連接地址**/
    public static final String BROKER_URL = "tcp://192.168.174.129:61616";

    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        receiver.receiveMessage();
    }

    /**
     * 接收消息
     *
     */
    public void receiveMessage () {

        Connection connection = null;
        Session session = null;
        MessageConsumer messageConsumer = null;

        try {
            //1.創建一個連接工廠
            ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);

            //2.創建一個連接
            connection = connectionFactory.createConnection();

            //3.創建一個Session
            session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

            //4.創建一個目的地
            Destination destination = session.createQueue("myQueue");

            //5.創建一個消息的消費者(接收者)
            messageConsumer = session.createConsumer(destination);

            //接收消息之前,需要把連接啓動一下
            connection.start();

            //6.接收消息 同步接收
            //Message message = messageConsumer.receive();

            //異步接收,使用監聽器接收消息
            messageConsumer.setMessageListener(new MessageListener(){
                public void onMessage(Message message) {
                    //判斷消息的類型
                    if (message instanceof TextMessage) { //判斷是否是文本消息
                        String text = null;
                        try {
                            text = ((TextMessage) message).getText();
                        } catch (JMSException e) {
                            e.printStackTrace();
                        }
                        System.out.println("接收到的消息內容是:" + text);
                    }
                }
            });
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            /*try {
                //關閉連接釋放資源
                if (null != messageConsumer) {
                    messageConsumer.close();
                }
                if (null != session) {
                    session.close();
                }
                if (null != connection) {
                    connection.close();
                }
            } catch (JMSException e) {
                e.printStackTrace();
            }*/
        }
    }
}
三.ActiveMQ的集成使用:

3.1ActiveMQ與Spring集成(以下是一個簡單的上手Demo):

鑑於發送端與接收端pom文件依賴相同,所以只寫一份,依賴如下:

        <!--spring-jms依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>

        <!--activemq的jar依賴-->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-client</artifactId>
            <version>5.15.2</version>
        </dependency>

        <!-- JMS規範的jar依賴 -->
        <dependency>
            <groupId>javax.jms</groupId>
            <artifactId>javax.jms-api</artifactId>
            <version>2.0.1</version>
        </dependency>
    


發送端:(需要兩個配置文件)

1. applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">

    <import resource="applicationContext-jms.xml"/>

</beans>
2. applicationContext-jms.xml

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

    <context:component-scan base-package="com.kinglong.activemq.sender"/>
    
    <!-- 配置一個連接工廠 -->
    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://192.168.174.129:61616"/>
    </bean>

    <!-- 配置JmsTemplate -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultDestinationName" value="springTopic" />
        <!--指定消息傳送模式,true表示發佈訂閱、false表示點對點-->
        <property name="pubSubDomain" value="true"/>
    </bean>

</beans>
發送類:

package com.kinglong.activemq.sender;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
@Component
public class Sender {

        @Autowired
        private JmsTemplate jmsTemplate;

        public void sendMessage (final String msg) {
            //發送消息
            jmsTemplate.send(new MessageCreator() {
                public Message createMessage(Session session) throws JMSException {
                    return session.createTextMessage(msg);
                }
            });
        }

}
下面是測試類:

package com.kinglong.activemq.sender;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
       public static void main(String[] args) {

            ClassPathXmlApplicationContext context =
                    new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

            Sender springSender = (Sender)context.getBean("sender");

            springSender.sendMessage("Spring jms ActiveMQ.");

        }
}

接收端(也同樣需要兩個配置文件,這裏使用異步接收,所以需要配置一個監聽器):

1. applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">

    <import resource="applicationContext-jms.xml"/>

</beans>
2. applicationContext-jms.xml

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

    <context:component-scan base-package="com.kinglong.activemq.receiver"/>
    
    <!-- 配置一個連接工廠 -->
    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://192.168.174.129:61616"/>
    </bean>

    <!-- 配置JmsTemplate -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultDestinationName" value="springTopic" />
        <!--指定消息傳送模式,true表示發佈訂閱、false表示點對點-->
        <property name="pubSubDomain" value="true"/>
    </bean>


    <!-- 我們自定義的一個消息監聽器 -->
    <bean id="receiverListener" class="com.kinglong.activemq.receiver.MyMessageListener" />

    <!-- 配置一個sping監聽器的容器 -->
    <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destinationName" value="springTopic"/>
        <property name="messageListener" ref="receiverListener" />
        <!--指定消息傳送模式,true表示發佈訂閱、false表示點對點-->
        <property name="pubSubDomain" value="true"/>
    </bean>

</beans>
監聽器:(MyMessageListener)
package com.kinglong.activemq.receiver;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class MyMessageListener implements MessageListener {

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            String text = null;
            try {
                text = ((TextMessage) message).getText();
            } catch (JMSException e) {
                e.printStackTrace();
            }
            System.out.println(text);
        }
    }

}
下面是測試類:

package com.kinglong.activemq.receiver;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

    }

}
說明:由於使用的是Topic發佈訂閱模式,所以測試時一定先開啓接收端服務,再開啓發送端發送消息,否則接不到消息.

相比較之下,spring集成的activemq使用已經相當簡化,與原始版相比,整個鏈接對象的創建全部交給spring完成了,所以效率上更高了.

其次,各屬性配置也都從代碼中剝離歸整在配置文件裏,使得修改配置也變得更爲容易,但是,個人覺得還是有必要學習學習原始版本,

可以更好地理解其創建過程,加深理解.


3.2ActiveMQ與Springboot集成(以下是一個簡單的上手Demo):

同樣pom依賴,因爲依賴相同,所以只寫一份,如下:

<!--配置父級依賴-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <!--屬性配置-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <!--開發springboot的Java程序的起步依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--springboot集成activemq的起步依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>

    </dependencies>
發送端:

只有一個主配置文件:application.properties

spring.activemq.broker-url=tcp://192.168.174.129:61616

#發佈訂閱模式
spring.jms.pub-sub-domain=true
main方法類:

package com.kinglong.activemq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SenderApplication {

    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(SenderApplication.class, args);

        SpringBootSender springBootSender = (SpringBootSender)context.getBean("springBootSender");

        springBootSender.sendMessage("springboot activemq.");
    }


}


發送者:SpringBootSender

package com.kinglong.activemq;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;

@Component
public class SpringBootSender {

    @Autowired //注入JmsTemplate
    private JmsTemplate jmsTemplate;

    //發送消息,"myTopic"是發送到的隊列destination,message是待發送的消息
    public void sendMessage(String message) {
        jmsTemplate.send("myTopic", new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                return session.createTextMessage(message);
            }
        });
    }
}
接收端:
主配置文件與發送端相同
main方法類:ReceiverApplication
package com.kinglong.activemq;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import javax.jms.JMSException;

@SpringBootApplication
public class ReceiverApplication {

    public static void main(String[] args) throws JMSException {

        ConfigurableApplicationContext context = SpringApplication.run(ReceiverApplication.class, args);

        //這裏採用異步接收
        
    }
}

監聽器:MessageListenerReceiver
package com.kinglong.activemq;

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

@Component
public class MessageListenerReceiver {

    @JmsListener(destination = "myTopic")
    public void receiveQueue(String text) {
        System.out.println("Consumer收到的報文爲:" + text);
    }
}
OVER! 是不是發現springboot集成的ActiveMQ更加精巧了?配置文件都不用寫了,springboot全幫我們做完了.不過,還是那句話,建議把原始版本原理,思路整通了再做spring或是springboot的集成,要不感覺直接理解起來springboot的發送和接收還是比較抽象的.


四.ActiveMQ集羣
4.1 何爲集羣?
集羣就是將相同的程序、功能,部署在兩臺或多臺服務器上,這些服務器對外提供的功能是完全一樣的。通過不斷橫向擴展增加服務器的方式,以提高服務的能力。

4.1.1 不集羣模式


4.1.2 集羣模式

4.2 集羣的優勢:

 1、集羣可以解決單點故障問題;

  2、集羣可以提高系統服務能力;









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