Wildcards(通配符)
Wildcars用來支持名字分層體系,它不是JMS規範的一部分,是ActiveMQ的擴展。
ActiveMQ支持以下三種通配符:
- “.”:用於作爲路徑上名字間的分隔符
- “>”:用於遞歸的匹配任何以這個名字開始的Destination(目的地)
- “*”:用於作爲路徑上任何名字。
舉例來說,如有以下兩個Destination:
PRICE.COMPUTER.JD.APPLE(蘋果電腦在京東上的價格)
PRICE.COMPUTER.TMALL.APPLE(蘋果電腦在天貓上的價格)
- PRICE.> :匹配任何產品的價格變動
- PRICE.COMPUTER.> :匹配任何電腦產品的價格變動
- PRICE.COMPUTER.JD.*:匹配任何在京東上的電腦的價格變動
- 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就可以監聽到消息了。