ActiveMQ學習第五篇:Destination的特性

Wildcards(通配符)

Wildcars用來支持名字分層體系,它不是JMS規範的一部分,是ActiveMQ的擴展。
  ActiveMQ支持以下三種通配符:

  • “.”:用於作爲路徑上名字間的分隔符
  • “>”:用於遞歸的匹配任何以這個名字開始的Destination(目的地)
  • “*”:用於作爲路徑上任何名字。
    舉例來說,如有以下兩個Destination:
      PRICE.COMPUTER.JD.APPLE(蘋果電腦在京東上的價格)
      PRICE.COMPUTER.TMALL.APPLE(蘋果電腦在天貓上的價格)
  1. PRICE.> :匹配任何產品的價格變動
  2. PRICE.COMPUTER.> :匹配任何電腦產品的價格變動
  3. PRICE.COMPUTER.JD.*:匹配任何在京東上的電腦的價格變動
  4. PRICE.COMPUTER.*.APPLE:匹配蘋果電腦京東或天貓上的價格變動
// 實例化連接工廠
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, "failover:(tcp://localhost:61616,tcp://localhost:61626)?randomize=false");
// 通過連接工廠獲取連接
Connection connection = connectionFactory.createConnection();
// 啓動連接
connection.start();
// 創建session
Session session = connection.createSession(Boolean.TRUE,Session.AUTO_ACKNOWLEDGE);
// 創建隊列
Destination destination = session.createQueue("PRICE.COMPUTER.JD.APPLE");
// 創建生產者
MessageProducer messageProducer = session.createProducer(destination);
for (int i = 1; i <= 10; i++) {
    TextMessage textMessage = session.createTextMessage(message);
    messageProducer.send("Mac Air價格:"  + i * 1000);
    System.out.println("發送消息 - " + textMessage.getText());
}
session.commit();
// 實例化連接工廠
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, "failover:(tcp://localhost:61616,tcp://localhost:61626)?randomize=false");
// 通過連接工廠獲取連接
Connection connection = connectionFactory.createConnection();
// 啓動連接
connection.start();
// 創建session
Session session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
// 創建隊列
Destination destination = session.createQueue("PRICE.COMPUTER.>");
// 創建消費者
MessageConsumer messageConsumer = session.createConsumer(destination);
messageConsumer.setMessageListener(new MessageListener(){
  @Override
    public void onMessage(Message message) {
        try {
            System.out.println("收到的消息:" + ((TextMessage) message).getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
});

通配符中是爲消費者服務的。即:通配符只能配置在消費端。

Composite Destinations(組合列隊)

組合隊列允許用一個虛擬的destination代表多個destinations。這樣就可以通過compositedestinations在一個操作中同時向多個queue發送消息。

客戶端實現的方式:

​ 在composite destinations中,多個destination之間採用“,”分割。例如:

        //創建一個隊列
       // Destination destination = session.createQueue("test,test1");
        Queue queue = new ActiveMQQueue("test,test1");
        //創建生產者
//        MessageProducer producer = session.createProducer(destination);
        MessageProducer producer = session.createProducer(queue);

如果你希望使用不同類型的destination,那麼需要加上前綴如queue:// 或topic://,例如:

Queue queue = new ActiveMQQueue("test,topic://192.168.100.155::61616");

在xml配置實現的方式:

<destinationInterceptors>
  <virtualDestinationInterceptor>
    <virtualDestinations>
        <!-- 虛擬的queue的名字-->
      <compositeQueue name="MY.QUEUE">
        <forwardTo>
            <!-- 實際發送的名稱 -->
          <queue physicalName="my-queue" />
          <queue physicalName="my-queue2" />
        </forwardTo>
      </compositeQueue>
    </virtualDestinations>
  </virtualDestinationInterceptor>
</destinationInterceptors>

使用filtered destinations,在xml配置如下:

<destinationInterceptors>
       <virtualDestinationInterceptor>
              <virtualDestinations>
                     <compositeQueue name="MY.QUEUE">
                            <forwardTo>
                                 <filteredDestination selector="odd='yes'" queue="FOO"/>
                                 <filteredDestination selector="i = 5" topic="BAR" />
                            </forwardTo>
                     </compositeQueue>
              </virtualDestinations>
       </virtualDestinationInterceptor>
</destinationInterceptors>

避免在network連接到broker,出現重複消息:

<networkConnectors>
<networkConnector uri= "static://(tcp://localhost:61616) " >
<excludedDestinations>
    <queue physicalName="Consumer.*VirtualTopic.> " />
</ excludedDestinations>
</ networkConnector>
</ networkConnectors>

在ActiveMQ啓動時候就創建Destination

<broker xmlns="http://activemq.apache.org/schema/core">
       <destinations>
              <queue physicalName="FOO.BAR" />
              <queue physicalName="SOME.TOPIC" />
       </destinations>
</broker>

Delete Inactive Destinations (刪除無用的隊列)

可以通過web控制檯或是JMX方式來刪除掉,通過配置文件,自動探測無用的隊列並刪除掉,回收響應資源,配置如下:
​ SchedulePeriodForDestinationPurge:設置多長時間檢查一次。
​ inactiveTimeoutBeforeGC:設置當destination爲空後,多長時間被刪除,這裏是30s,默認爲60
​ gcInactiveDestinations:設置刪除掉不活動隊列,默認爲false

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulePeriodForDestinationPurge="10000">
       <destinationPolicy>
              <policyMap>
                <policyEntries>
                     <policyEntry queue=">" gcInactiveDestinations="true" inactiveTimeoutBeforeGC="30000" />
                </policyEntries>
              </policyMap>
       </destinationPolicy>
</broker>

Destination options (隊列選項)

隊列選項是給consumer在JMS規範之外添加的功能特性,通過在隊列名稱後面使用類似URL的語法添加多個選項。包括:
1:consumer.prefetchSize,consumer持有的未確認最大消息數量,默認值 variable。
2:consumer.maximumPendingMessageLimit:用來控制非持久化的topic在存在慢消費者的情況下,丟棄的數量,默認0。
3:consumer.noLocal :默認false。
4:consumer.dispatchAsync :是否異步分發 ,默認true。
5:consumer.retroactive:是否爲回溯消費者 ,默認false。
6:consumer.selector:Jms的Selector,默認null。
7:consumer.exclusive:是否爲獨佔消費者 ,默認false。
8:consumer.priority:設置消費者的優先級,默認0。

queue = new ActiveMQQueue("PRICE.COMPUTER.TMALL.APPLE?consumer.dispatchAsync=true&consumer.prefetchSize=20");
consumer = session.createConsumer(queue);

Visual Destinations

前面也說到了兩個虛擬主題,虛擬Destinations和組合Destinations
  ActiveMQ中,topic只有在持久訂閱下才是持久化的。持久訂閱時,每個持久訂閱者,都相當於一個queue的客戶端,它會收取所有消息。這種情況下存在兩個問題:
  (1) 同一應用內consumer端負載均衡的問題:即同一個應用上的一個持久訂閱不能使用多個consumer來共同承擔消息處理功能。因爲每個consumer都會獲取所有消息。queue模式可以解決這個問題,但broker端又不能將消息發送到多個應用端。所以,既要發佈訂閱,又要讓消費者分組,這個功能JMS規範本身是沒有的。
在這裏插入圖片描述
(2)同一應用內consumer端failover的問題:由於只能使用單個的持久訂閱者,如果這個訂閱者出錯,則應用就無法處理消息了,系統的健壯性不高。
  Activemq可以實現虛擬的Topic來解決這兩個問題。

使用虛擬主題:

對於消息發佈者來說,就是一個正常的Topic,名稱以VirtualTopic.開頭。例如VirtualTopic.Mobile。示例:

Topic destination = session.createTopic("VirtualTopic.Mobille");

  對於消息接收端來說,是個隊列,不同應用裏使用不同的前綴作爲隊列的名稱,即可表明自己的身份即可實現消費端應用分組。

  如Consumer.A.VirtualTopic.Mobille,說明它是名稱爲A的消費端,同理Consumer.B.VirtualTopic.Mobille說明是一個名稱爲B的客戶端。可以在同一個應用裏使用多個consumer消費此Topic,則可以實現上面兩個功能。

  又因爲不同應用使用的queue名稱不同(前綴不同),所以不同的應用中都可以接收到全部的消息。代碼示例如下:

public static void main(String[] args) throws JMSException {
        ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.100.155:61616");
        Connection connection = factory.createConnection();
        Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        //創建虛擬主題,加前綴VirtualTopic
        Topic topic = session.createTopic("VirtualTopic.TestTopic");
        MessageProducer producer = session.createProducer(topic);
        producer.setDeliveryMode(DeliveryMode.PERSISTENT);
        connection.start();
        for (int i = 0; i < 30; i++) {
            TextMessage textMessage = session.createTextMessage("topic消息===" + i);
            producer.send(textMessage);
        }
        session.commit();
        connection.close();
    }

 public static void main(String[] args) throws JMSException {
        ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.100.155:61616");
        Connection connection = factory.createConnection();
        connection.start();
        final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        //創建隊列
        Destination destination = session.createQueue("Consumer.A.VirtualTopic.TestTopic");
        MessageConsumer consumer = session.createConsumer(destination);
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                TextMessage textMessage = (TextMessage) message;
                try {
                    System.out.println("Consumer.A.接收到得消息:" + textMessage.getText());
                    session.commit();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });
    }

 public static void main(String[] args) throws JMSException {
        ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.100.155:61616");
        Connection connection = factory.createConnection();
        connection.start();
        final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        //創建隊列
        Destination destination = session.createQueue("Consumer.B.VirtualTopic.TestTopic");
        final MessageConsumer consumer = session.createConsumer(destination);
        final MessageConsumer messageConsumer = session.createConsumer(destination);
        //模擬多個consumer消費一個queue
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    consumer.setMessageListener(new MessageListener() {
                        @Override
                        public void onMessage(Message message) {
                            TextMessage textMessage = (TextMessage) message;
                            try {
                                System.out.println("Consumer.B-->consumer接收到消息:" + textMessage.getText());
                                session.commit();
                            } catch (JMSException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    messageConsumer.setMessageListener(new MessageListener() {
                        @Override
                        public void onMessage(Message message) {
                            TextMessage textMessage = (TextMessage) message;
                            try {
                                System.out.println("Consumer.B-->messageConsumer接收到消息:" + textMessage.getText());
                                session.commit();
                            } catch (JMSException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

在接收消息之前,應該先運行一下consumer客戶端,將消費者註冊到Broker中。
xml配置:
默認虛擬主題的前綴是: VirtualTopic.>
自定義消費虛擬地址默認格式: Consumer.*.VirtualTopic.>
自定義消費虛擬地址可以改,比如下面的配置就把它修改了。

<destinationInterceptors>
        <virtualDestinationInterceptor>
            <virtualDestinations>
                <virtualTopic name=">" prefix="自定義前綴.*." selectorAware="false"/>
            </virtualDestinations>
        </virtualDestinationInterceptor>
</destinationInterceptors>

Mirrored Queules(鏡像隊列)

  ActiveMQ中每個queue中的消息只能被一一個consumer消費。然而,有時候你可能希望能夠監視生產者和消費者之間的消息流。你可以通過使用Virtual Destinations 來建立一個virtual queue來把消息轉發到多個queues中。但是爲系統中每個queue都進行如此的配置可能會很麻煩。
  ActiveMQ支持Mirrored Queues。 Broker 會把發送到某個queue的所有消息轉發到一個名稱類似的topic,因此監控程序只需要訂閱這個mirrored queue topic。 爲了啓用Mirrored Queues,首先要將BrokerService的useMirroredQueues屬性設置成true,然後可以通過destinationInterceptors設置其它屬性,如mirrortopic的前綴, 缺省是:“VirtualTopic. Mirror."
在這裏插入圖片描述

<destinationInterceptors>
    <mirroredQueue copyMessage = "true" postfix=".qmirror" prefix=""/>
</destinationInterceptors>

在這裏插入圖片描述

這樣發送之後會自動存放到一個topic裏面。需要定於那個topic就可以監聽到消息了。
在這裏插入圖片描述
在這裏插入圖片描述

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