Topic exchange
消息發送到topic exchange(主題交換器)不能具有任意的routing_key(路由鍵),它必須是一系列的單詞,使用點號.分隔。這些單詞可以是任意的,但是經常定義了與消息相關的特性。一些有效的路由鍵示例:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit"。路由鍵中的字可以任意多個,最多255個字節。
綁定的鍵必須是相同的形式。主題交換器(topic exchange)背後的邏輯和直接交換器(direct exchange)的邏輯是相似的,一個帶特別路由鍵的消息會被髮送到與之路由鍵匹配的隊列。對於路由鍵有兩種重要的特殊情況:
1、*代替一個單詞
2、#代替零個或多個單詞
使用以下例子能很容易地解釋以上情況
在這個例子裏,我們會給所有描述的動物發送消息。這些消息使用三個單詞(兩個點號)組成的路由鍵進行發送。第一個單詞描述速度,第二個單詞描述顏色,第三個代表種類:
"<speed>.<colour>.<species>"
我們創建三個綁定,Q1綁定”*.orange.*”,Q2綁定”*.*.rabbit”和”lazy.#”。
這些綁定可以通過以下兩點概況:
1、 Q1對橘黃色的動物感興趣
2、 Q2想要傾聽任何關於rabbit,任何關於lazy 動物的東西。
路由鍵設置爲”quick.orange.rabbit”的消息將被髮送到兩個隊列。消息”lazy.orange.elephant”也將被髮送到兩個隊列。另一方面,”quick.orange.fox”將被髮送到Q1隊列,“lazy.brown.fox”只會被髮送到Q2對列。”lazy.pink.rabbit”將只會被髮送到Q2隊列一次,雖然它匹配兩個綁定。”quick.brown.fox”不能匹配任何綁定,將會被丟棄。
如果我們破壞規則,發送1個或4個單詞的消息會發送什麼,比如”orange”或者”quick.orange.male.rabbit”?很好,這些消息不會匹配任何的綁定,將會丟失。
另一方面,”lazy.orange.male.rabbit”,雖然有四個單詞,但是它匹配了最後的綁定(lazy.#),會被髮送到第二個隊列。
Topic exchange功能強大,可以做其他交換器做的事情。
當一個對列使用#作爲綁定鍵,它就可以接受任何消息,無視路由鍵,就像fanout exchange(扇形交換器)。
當對列在綁定鍵中不使用*或者#,那麼topic exchange(主題交換器)就像一個direct exchange(直接交換器)。
實例,以下消費者實例不是同時啓動,是分別運行,每次只運行一個消費者實例
生產者
發送消息:
Send quick.orange.rabbit:Hello,RabbitMq1
Send lazy.orange.elephant:Hello,RabbitMq2
Send quick.orange.fox:Hello,RabbitMq3
Send lazy.brown.fox:Hello,RabbitMq4
Send lazy.pink.rabbit:Hello,RabbitMq5
Send quick.brown.fox:Hello,RabbitMq6
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class TestProducerTopic {
public final static String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args)
throws IOException, TimeoutException {
/* 創建連接,連接到RabbitMQ*/
Connection connection = RabbitmqConnection.getConnection();
/*創建信道*/
Channel channel = connection.createChannel();
/*創建topic交換器*/
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
/*日誌消息級別,作爲路由鍵使用*/
String[] routekeys = {
"quick.orange.rabbit"//匹配*.orange.*
,"lazy.orange.elephant"//匹配lazy.#,
,"quick.orange.fox"
,"lazy.brown.fox"
,"lazy.pink.rabbit"
,"quick.brown.fox"};
for(int i=0;i<routekeys.length;i++){
String routekey = routekeys[i];
String msg = "Hello,RabbitMq"+(i+1);
/*發佈消息,需要參數:交換器,路由鍵,其中以日誌消息級別爲路由鍵*/
channel.basicPublish(EXCHANGE_NAME,routekey,null,
msg.getBytes());
System.out.println("Send "+routekey+":"+msg);
}
channel.close();
connection.close();
}
}
消費者A
接受到消息:
Received[quick.orange.rabbit]Hello,RabbitMq1
Received[lazy.orange.elephant]Hello,RabbitMq2
Received[quick.orange.fox]Hello,RabbitMq3
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*類說明:普通的消費者
*/
public class TestConsumerTopicA {
public static void main(String[] argv)
throws IOException, TimeoutException {
// 打開連接和創建頻道,與發送端一樣
Connection connection = RabbitmqConnection.getConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(TestProducerTopic.EXCHANGE_NAME,
"topic");
/*聲明一個隊列*/
String queueName = "orange";
channel.queueDeclare(queueName,false,false,
false,null);
/*綁定,將隊列和交換器通過路由鍵進行綁定*/
String routekey = "*.orange.*";
channel.queueBind(queueName, TestProducerTopic.EXCHANGE_NAME,routekey);
System.out.println("waiting for message........");
/*聲明瞭一個消費者*/
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Received["+envelope.getRoutingKey()
+"]"+message);
}
};
/*消費者正式開始在指定隊列上消費消息*/
channel.basicConsume(queueName,true,consumer);
}
}
消費者B
接收消息:
Received[quick.orange.rabbit]Hello,RabbitMq1
Received[lazy.pink.rabbit]Hello,RabbitMq5
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*類說明:普通的消費者
*/
public class TestConsumerTopicB {
public static void main(String[] argv)
throws IOException, TimeoutException {
// 打開連接和創建頻道,與發送端一樣
Connection connection = RabbitmqConnection.getConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(TestProducerTopic.EXCHANGE_NAME,
"topic");
/*聲明一個隊列*/
String queueName = "rabbit";
channel.queueDeclare(queueName,false,false,
false,null);
/*綁定,將隊列和交換器通過路由鍵進行綁定*/
String bindKey = "*.*.rabbit";
channel.queueBind(queueName, TestProducerTopic.EXCHANGE_NAME,bindKey);
System.out.println("waiting for message........");
/*聲明瞭一個消費者*/
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Received["+envelope.getRoutingKey()
+"]"+message);
}
};
/*消費者正式開始在指定隊列上消費消息*/
channel.basicConsume(queueName,true,consumer);
}
}
消費者C
Received[lazy.orange.elephant]Hello,RabbitMq2
Received[lazy.brown.fox]Hello,RabbitMq4
Received[lazy.pink.rabbit]Hello,RabbitMq5
package com.enjoy.testrabbitmq;
import com.enjoy.common.RabbitmqConnection;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*類說明:普通的消費者
*/
public class TestConsumerTopicC {
public static void main(String[] argv)
throws IOException, TimeoutException {
// 打開連接和創建頻道,與發送端一樣
Connection connection = RabbitmqConnection.getConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(TestProducerTopic.EXCHANGE_NAME,
"topic");
/*聲明一個隊列*/
String queueName = "lazy";
channel.queueDeclare(queueName,false,false,
false,null);
/*綁定,將隊列和交換器通過路由鍵進行綁定*/
String bindKey = "lazy.#";
channel.queueBind(queueName, TestProducerTopic.EXCHANGE_NAME,bindKey);
System.out.println("waiting for message........");
/*聲明瞭一個消費者*/
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println("Received["+envelope.getRoutingKey()
+"]"+message);
}
};
/*消費者正式開始在指定隊列上消費消息*/
channel.basicConsume(queueName,true,consumer);
}
}