RabbitMQ 實戰教程(四) 路由

在本教程中,我們將添加一個功能,讓日誌接收者能夠訂閱部分消息。例如,我們將能夠直接將錯誤寫入日誌文件(以節省磁盤空間),仍然能夠在控制檯上打印所有的日誌消息。

綁定(Bindings)

在上一個教程中,我們已經使用過綁定。類似下面的代碼

channel.queueBind(queueName, EXCHANGE_NAME, "");

綁定表示轉發器與隊列之間的關係。我們也可以簡單的認爲:隊列對該轉發器上的消息感興趣。

綁定可以附帶一個額外的參數routingKey。爲了與避免basicPublish方法(發佈消息的方法)的參數混淆,我們準備把它稱作綁定鍵(binding key) 。下面展示如何使用綁定鍵(binding key)來創建一個綁定。

channel.queueBind(queueName, EXCHANGE_NAME, "black");
  1. 綁定鍵的意義依賴於轉發器的類型。對於fanout類型,忽略此參數。

直接轉發(Direct exchange)

在上一個教程中,日誌系統廣播所有的消息給所有的消費者。現在,我們可能希望把錯誤日誌寫入硬盤,而不把硬盤空間浪費在警告或者消息類型的日誌上。之前我們使用fanout類型的轉發器,這並沒有給我們太多的靈活性,它只能盲目的廣播。

所以,我們將會使用direct類型的轉發器進行替代。direct類型的轉發器背後的路由轉發算法很簡單:消息會被推送至綁定鍵(binding key)和消息發佈附帶的選擇鍵(routing key)完全匹配的隊列。

我們可以看到direct類型的轉發器與兩個隊列綁定。第一個隊列與綁定鍵orange綁定,第二個綁定有兩個綁定,一個與綁定鍵black綁定,另一個與green綁定鍵綁定。在這樣的設置的情況下,當一個消息附帶一個選擇鍵(routing key) orange發佈至轉發器將會被導向到隊列Q1。消息附帶一個選擇鍵(routing key)black或者green將會被導向到隊列Q2。所有其他消息將被丟棄。

多重綁定(Multiple bindings)


使用一個綁定鍵(binding key)綁定多個隊列是完全合法的。在這種情況下,一個附帶選擇鍵(routing key)的消息將會被轉發到Q1和Q2。

發送日誌(Emitting logs)

我們準備將這種模式用於我們的日誌系統。我們將消息發送到direct類型的轉發器而不是fanout類型。我們將把日誌的嚴重性作爲選擇鍵(routing key) 。這樣的話,接收程序可以根據嚴重性來選擇接收。我們首先關注發送日誌的代碼:

channel.exchangeDeclare(EXCHANGE_NAME,"direct");

 

現在,我們準備發送一條消息

channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());

爲了簡化代碼,我們假定‘severity’是‘info’,‘warning’,‘error’中的一個。

訂閱(Subscribing)

接收消息將工作就像在以前的教程中,只有一點不同,我們給我們所感興趣的嚴重性類型的日誌創建一個綁定。

String queueName = channel.queueDeclare().getQueue();
for(String severity : argv){
  channel.queueBind(queueName, EXCHANGE_NAME, severity);
}

案例實戰

發送端,連接到RabbitMQ(此時服務需要啓動),發送一條數據,然後退出。

import java.io.IOException;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
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";
private static final String[] LOG_LEVEL_ARR = {"debug", "info", "error"};
public static void main(String[] args) throws IOException, TimeoutException {
// 創建連接
ConnectionFactory factory = new ConnectionFactory();
// 設置MabbitMQ, 主機ip或者主機名
factory.setHost("127.0.0.1");
// 創建一個連接
Connection connection = factory.newConnection();
// 創建一個通道
Channel channel = connection.createChannel();
// 指定一個轉發器
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 發送消息
for (int i = 0; i < 10; i++) {
int rand = new Random().nextInt(3);
String severity = LOG_LEVEL_ARR[rand];
String message = "somnus-MSG log : [" +severity+ "]" + UUID.randomUUID().toString();
// 發佈消息至轉發器
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
System.out.println(" [x] 發送消息是: '" + message + "'");
}
// 關閉連接
channel.close();
connection.close();
}
}

接受端,不斷等待服務器推送消息,然後在控制檯輸出。

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class ReceiveLogsDirect {
private static final String EXCHANGE_NAME = "direct_logs";
private static final String[] LOG_LEVEL_ARR = {"debug", "info", "error"};
public static void main(String[] args) throws IOException, TimeoutException {
// 創建連接
ConnectionFactory factory = new ConnectionFactory();
// 設置MabbitMQ, 主機ip或者主機名
factory.setHost("127.0.0.1");
// 創建一個連接
Connection connection = factory.newConnection();
// 創建一個通道
Channel channel = connection.createChannel();
// 指定一個轉發器
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 設置日誌級別
int rand = new Random().nextInt(3);
String severity = LOG_LEVEL_ARR[rand];
// 創建一個非持久的、唯一的、自動刪除的隊列
String queueName = channel.queueDeclare().getQueue();
// 綁定轉發器和隊列
channel.queueBind(queueName, EXCHANGE_NAME, severity);
// 打印
System.out.println(" [*] 等待消息進入. 請按 CTRL+C 結束");
System.out.println(" [*] LOG LEVEL : " + severity);
// 創建隊列消費者
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(" [x] 接收消息是:'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}

首先,我們開啓三個ReceiveLogsDirect。然後,啓動EmitLogDirect進行消息發送。打印結果如下

ReceiveLogsDirect1

[*] 等待消息進入. 請按 CTRL+C 結束
[*] LOG LEVEL : info
[x] 接收消息是: 'somnus-MSG log : [info]0dd0ae0c-bf74-4aa9-9852-394e65fbf939'
[x] 接收消息是: 'somnus-MSG log : [info]b2b032f6-e907-4c95-b676-1790204c5f73'
[x] 接收消息是: 'somnus-MSG log : [info]14482461-e432-4866-9eb5-a28f4edeb47f'

 

ReceiveLogsDirect2

[*] 等待消息進入. 請按 CTRL+C 結束
[*] LOG LEVEL : error
[x] 接收消息是: 'somnus-MSG log : [error]493dce2a-7ce1-4111-953c-99ab2564a2d0'
[x] 接收消息是: 'somnus-MSG log : [error]2446dd80-d5f0-4d39-888f-31579b9d2724'
[x] 接收消息是: 'somnus-MSG log : [error]fe8219e0-5548-40ba-9810-d922d1b03dd8'
[x] 接收消息是: 'somnus-MSG log : [error]797b6d0e-9928-4505-9c76-56043322b1f0'
  1.  

ReceiveLogsDirect3

[*] 等待消息進入. 請按 CTRL+C 結束
[*] LOG LEVEL : debug
[x] 接收消息是: 'somnus-MSG log : [debug]c05eee3e-b820-4b69-9c3f-c2bbded85195'
[x] 接收消息是: 'somnus-MSG log : [debug]4645c9ba-4070-41d7-adc9-7f8b2df1e3c8'
[x] 接收消息是: 'somnus-MSG log : [debug]d3d3ad5c-8f97-49ea-8fd6-c434790e40eb'

 

我們發現,ReceiveLogsDirect1、ReceiveLogsDirect2、ReceiveLogsDirect3同時收到了屬於自己級別的消息。

 

相關連接:

RabbitMq3.6官方文檔

發佈了69 篇原創文章 · 獲贊 172 · 訪問量 60萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章