公式文書アドレス: 4ルーティング
メッセージを選択的に受信します。
前提条件
このチュートリアルは、RabbitMQがインストールされており、ローカルホストポート(5672)で実行されていることを前提としています。
ルーティング
前のチュートリアルでは、簡単なログシステムを構築しました。ログメッセージを多くの受信者にブロードキャストできます。
このチュートリアルでは、それに機能を追加します-メッセージのサブセットのみをサブスクライブします。たとえば、重要なエラーメッセージのみをログファイルに送信し(ディスク領域を節約するため)、コンソールにすべてのログメッセージを出力することはできます。
練る
前の例では、バインディングを作成しました。次のようにコードを思い出してください。
channel.queueBind(queueName, EXCHANGE_NAME, "");
バインディングは、スイッチとキューの間の関係です。これは、次のように簡単に理解できます。キューはこの交換からのメッセージに関心があります。
バインディングは追加のroutingKey
パラメーターを使用できます。basic_publish
パラメータとの混同を避けるために、これをと呼びますbinding key
。バインディングキーを作成します。
channel.queueBind(queueName, EXCHANGE_NAME, "black");
バインディングキーの役割は、exchange
タイプによって異なります。以前に使用したfanout
スイッチは、その値を無視します。
ダイレクトスイッチ
前のチュートリアルのログシステムは、すべてのユーザーにメッセージをブロードキャストします。これを拡張して、重大度に基づいてメッセージをフィルタリングできるようにします。たとえば、error
レベルのログメッセージのみをディスクに書き込み、メッセージをディスクに書き込んwarning
だりinfo
ログに記録したりしたくない場合があります。
私たちはfanout
スイッチを使用していますが、これはあまり柔軟性がなく、盲目的にしかブロードキャストできません。
我々はなりますdirect
それを交換してスイッチ。direct
スイッチの背後にあるルーティングアルゴリズムは単純です。メッセージは、バインディングbinding key
キーがメッセージのルーティングキーとrouting key
完全に一致するキューに入ります。
この点を説明するために、次の設定について考えてみます。
この設定では、direct
交換がX
2つのキューにバインドされていることがわかります。最初のキューはバインディングキーorange
でバインドされ、2番目のキューには2つのバインディングがあります。1つはバインディングキーblack
で、もう1つはバインディングキーでバインドされますgreen
。
このような設定では、ルーティングキーorange
を使用してスイッチに投稿されたメッセージはキューにルーティングされますQ1
。メッセージ付きblack
またはgreen
ルーティングキーがに行きますQ2
。その他のメッセージは破棄されます。
マルチバインディング
同じバインディングキーで複数のキューをバインドすることは完全に合法です。この例では、X
とのQ1
間にバインディングキーを追加できblack
ます。この場合、direct
交換は交換のようにfanout
機能し、一致するすべてのキューにメッセージをブロードキャストします。ルーティングキーblack
を含むメッセージはとに送信さQ1
れQ2
ます。
問題のログ
このモデルをロギングシステムで使用します。メッセージは、direct
取引所ではなく取引所に送信されますfanout
。例としてログの重大度を示しrouting key
ます。このようにして、受信プログラムは重大度に基づいてログを選択的に受信できるようになります。まず、ログを発行するコードを見てみましょう。
いつものように、最初にスイッチを作成する必要があります。
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
メッセージを送信する準備ができました:
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
簡素化の問題のために、我々は、ログレベルがあることを前提とseverity
することができinfo
、warning
、error
。
サブスクリプション
メッセージの受信は、1つの例外を除いて、前のチュートリアルと同じように機能します。関心のある重大度ごとに新しいバインディングを作成します。
String queueName = channel.queueDeclare().getQueue();
for(String severity : argv){
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
それらをまとめる
EmitLogDirect.java
クラスコード:
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author wangbo
* @date 2019/10/23 11:24
*/
public class EmitLogDirect {
//交换器名称
private final static String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接器连接到服务器
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try(Connection connection = factory.newConnection()){
//创建一个通道
Channel channel = connection.createChannel();
//声明交换器,设置交换器类型
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//从命令行接受参数
//日志级别,路由键
String severity = getSeverity(args);
//日志消息
String message = getMessage(args);
//发布消息到交换器,并设置路由键
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + 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();
}
}
ReceiveLogsDirect.java
クラスコード:
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author wangbo
* @date 2019/10/22 18:25
*/
public class ReceiveLogsDirect {
//交换器名称
private final static String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个连接器连接到服务器
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();
//将临时队列和交换器绑定,进行多重绑定
if (args.length < 1) {
System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
System.exit(1);
}
for (String severity : args) {
channel.queueBind(queueName, EXCHANGE_NAME, severity);
}
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 '" + message + "'");
};
//消费者监听
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
});
}
}
通常どおりにコンパイルします(コンパイルとクラスパスの提案については、チュートリアル1を参照してください)。便宜上、例を実行するときは、クラスパスの環境変数$CP
(つまりWindows
、上記%CP%
)を使用します。
javac -cp $CP ReceiveLogsDirect.java EmitLogDirect.java
情報をファイルに保存warning
してerror
(ではなくinfo
)ログに記録したいだけの場合は、コンソールを開いて次のように入力します。
java -cp $CP ReceiveLogsDirect warning error > logs_from_rabbit.log
画面にすべてのログ情報を表示する場合は、新しいターミナルを開きます。
java -cp $CP ReceiveLogsDirect info warning error
# => [*] Waiting for logs. To exit press CTRL+C
たとえば、error
ログメッセージを発行するには、次のように入力します。
java -cp $CP EmitLogDirect error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'
5
パターンに基づいてメッセージを聞く方法を学ぶためにレッスンを読み続けてください。