在上一個教程中,我們構建一個fanout類型的交換機,它能夠向許多接收者廣播消息。在本教程中我們將學習direct類型的交換機,向其中添加功能然後使僅訂閱消息的子集成爲可能。例:我們將只能將帶有error標記的消息定向到A隊列,其他標誌的消息定向到B隊列。
爲了能夠實現上面的功能,direct類型的交換機帶有一個參數來實現。routingKey被稱爲綁定鍵,綁定密鑰的含義取決於交換類型。我們之前使用的fanout交換隻是忽略了它的價值。
// 這是fanout類型交換機綁定隊列的代碼聲明
channel.queueBind(queueName, EXCHANGE_NAME, "");
// 這是direct類型交換機綁定隊列的代碼聲明
channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
爲什麼交換機存在direct類型的,fanout不夠用嗎?
上一教程中的Demo中我們將所有消息廣播給所有使用者,我們想要擴展它以允許根據郵件的嚴重性過濾郵件。例如,我們可能希望將日誌消息寫入磁盤的程序僅接收嚴重錯誤,而不會在警告或信息日誌消息上浪費磁盤空間。我們使用的是fanout交換,這種交換並沒有給我們帶來太大的靈活性,因爲它只能進行無意識的廣播。這裏我們將使用direct交換,direct交換背後的路由算法很簡單,消息進入其綁定密鑰與消息的路由密鑰完全匹配的隊列 。
我們舉個簡單例子的僞代碼:
// 定義一個direct交換機和A、B兩個隊列
channel.exchangeDeclare(exchange='first', type='direct')
channel.queueDeclare(queue='A')
channel.queueDeclare(queue='B')
// A隊列使用a綁定鍵,B隊列使用b綁定鍵
channel.queueBind(exchange='first', queue='A', routingKey='error')
channel.queueBind(exchange='first', queue='B', routingKey='warning')
// 發送消息
channel.basicPublish(exchange='first', routingKey='error', body='Hello World!')
channel.basicPublish(exchange='first', routingKey='warning', body='Hello World!')
這個時候我只需把error隊列的消息寫入磁盤便於我們去查看項目哪裏出錯,而warning這些信息不太重要,不需要存在磁盤就在B隊列放着讓編譯器在控制檯打印出來就行了。這種方法可以減少磁盤的不必要的存儲消耗和讀寫性能。
接下來我們貼具體代碼:
這是本節的demo代碼地址:https://gitee.com/mjTree/javaDevelop/tree/master/testDemo
package com.mytest.rabbitMQ.Fourth;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class EmitLogDirect {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
// 模擬命令行提供參數
//String[] arg = {};
//String[] arg = {"warning", "nothing"};
String[] arg = {"error", "Run or it will explode."};
String severity = getSeverity(arg);
String message = getMessage(arg);
channel.basicPublish(EXCHANGE_NAME, severity,
null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
}
}
private static String getSeverity(String[] strings) {
if (strings.length < 1)
return "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) {
int length = strings.length;
if (length == 0) return "";
if (length <= startIndex) return "";
StringBuilder words = new StringBuilder(strings[startIndex]);
for (int i = startIndex + 1; i < length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}
package com.mytest.rabbitMQ.Fourth;
import com.rabbitmq.client.*;
public class ReceiveLogsDirect {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
String queueName = channel.queueDeclare().getQueue(); // 隨機生成隊列
String queueName1 = channel.queueDeclare().getQueue();
// 模擬命令行提供參數
//String[] arg = {};
String[] arg = {"info", "warning", "error"};
if (arg.length < 1) {
System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
System.exit(1);
}
// 交換機和隊列綁定
channel.queueBind(queueName, EXCHANGE_NAME, arg[2]); // 把error綁在第一個隊列
// info和warning綁在第二個隊列中
channel.queueBind(queueName1, EXCHANGE_NAME, arg[0]);
channel.queueBind(queueName1, EXCHANGE_NAME, arg[1]);
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 -> { });
channel.basicConsume(queueName1, true, deliverCallback, consumerTag -> { });
}
}
先執行執行ReceiveLogsDirect,然後再對EmitLogDirect執行三次生成三個標誌消息,三次執行過程的arg都要不一樣,分別對arg註釋執行。