上一節教程中改進過程中,我們沒有使用只能進行虛擬廣播的fanout交換機,而是使用direct交換機,並有可能選擇性地接收消息。儘管使用direct交換改進了我們的系統,但它仍然存在侷限性,不能基於多個條件進行路由。
如果我們可能不僅要根據嚴重性訂閱日誌,還要根據發出日誌的源訂閱日誌。爲了實現這一點,我們需要學習更復雜的topic交換。
Topic交流
topic交換機的routingKey是單詞列表,以點分隔。這些詞可以是任何東西,但是通常它們指定與消息相關的某些功能。一些有效的路由關鍵示例:“ stock.usd.nyse ”,“ nyse.vmw ”,“ quick.orange.rabbit ”。路由密鑰中可以包含任意多個單詞,最多255個字節。綁定密鑰也必須採用相同的形式。主題交換背後的邏輯類似於直接交換的邏輯,使用特定路由密鑰發送的消息將被傳遞到所有使用匹配綁定密鑰綁定的隊列。綁定鍵有兩個重要的特殊情況:
*:可以代替一個單詞。
#:可以替代零個或多個單詞。
對於上面的匹配規則,我們舉幾個例子:
路由鍵名稱 能匹配的隊列
"quick.orange.rabbit": Q1和Q2
"lazy.orange.elephant": Q1和Q2
"quick.orange.fox": Q1
"lazy.brown.fox": Q2
"lazy.pink.rabbit": Q2
"quick.brown.fox": 都不匹配,被丟棄
"quick.orange.male.rabbit": 都不匹配,被丟棄
"lazy.orange.male.rabbit": Q2
Topic Exchange
主題交換功能強大,可以像其他交流一樣進行。
當隊列用" # "綁定鍵綁定時,它將接收所有消息,而與路由鍵無關,就像在fanout交換中一樣。
當在綁定中不使用特殊字符" * "和" # "時,主題交換的行爲就像direct的一樣。
接下來我們貼代碼:
package com.mytest.rabbitMQ.Fifth;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class EmitLogTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
String[] arg = {"kern.critical", "A critical kernel error"};
String routingKey = getRouting(arg);
String message = getMessage(arg);
channel.basicPublish(EXCHANGE_NAME, routingKey,
null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");
}
}
private static String getRouting(String[] strings) {
if (strings.length < 1)
return "anonymous.info";
return strings[0];
}
private static String getMessage(String[] strings) {
if (strings.length < 2)
return "Hello World!";
return joinStrings(strings, " ", 1);
}
private static String joinStrings(String[] strings, String delimiter, int startIndex) {
if (strings.length == 0) return "";
if (strings.length < startIndex) return "";
StringBuilder words = new StringBuilder(strings[startIndex]);
for (int i = startIndex + 1; i < strings.length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}
package com.mytest.rabbitMQ.Fifth;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
public class ReceiveLogsTopic {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
String queueName = channel.queueDeclare().getQueue();
String[] arg = {"kern.*", "*.critical"};
if (arg.length < 1) {
System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
System.exit(1);
}
for (String bindingKey : arg) {
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
}
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println("[x] Received '" +
delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
};
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
}
}
代碼大體和上節教程差不多。
這是本節的demo代碼地址:https://gitee.com/mjTree/javaDevelop/tree/master/testDemo