前回の記事では、以前のロギングシステムを少し改善しました。ブロードキャストにファンアウトタイプの交換を使用する代わりに、ダイレクトタイプの交換を使用して、ログメッセージを選択的に受信します。
直接タイプの交換を使用するとロギングシステムが改善されますが、それでもいくつかの制限があります(メッセージは複数の要素に基づいてルーティングできません)。
私たちのロギングシステムでは、ログレベルに従ってサブスクライブするだけでなく、指定されたルーティングキーに従ってサブスクライブすることも期待しています。UNIXのシステムログツールと同様に、ログメッセージのルーティングルールはログレベル(情報/警告/クリティカル...)だけでなく、デバイス(認証/クローン/カーネル...)にも基づいていることを理解する必要があります。
これにより柔軟性が大幅に向上します。たとえば、kernによってプッシュされたエラーレベルのログのみを聞くことができます。
このような機能をロギングシステムに実装するには、トピックタイプの交換についてさらに学ぶ必要があります。
1.トピック型交換
メッセージがトピックタイプの交換に送信される場合、routing_keyを任意に指定することはできません(ドットで接続された一連の文字列を参照する必要があります。単語は任意でかまいませんが、一般的にはメッセージに関連しています)。ルーティングキーの長さは255バイトを超えることはできません。
バインディングキーは同じ方法である必要があります。トピックタイプの交換は直接交換のようなものです。ルーティングキーを決定するためにプロデューサによって指定されたメッセージは、バインディングキーが一致できるすべてのコンシューマにプッシュされます。ただし、このバインディングには2つの特殊なケースがあります。
- *(アスタリスク):単語に一致する(のみ)ことができます
- #(ポンド記号):複数の単語(またはゼロ)に一致できます
次に例を示します。
この例では、動物について説明するメッセージを送信します。ルーティングキーの最初の単語は速度を表し、2番目の単語は色を表し、3番目の単語は種を表します: ".."。
「バインディングキーは次のとおりです。ここでは、バインディングの3を作成します。.Orange Q1の」、および結合キーがある「怠惰な#「Q2の.rabbit」そして」。
これらのバインディングは、次のように要約できます。
- Q1はすべてのオレンジ色の動物に関心があります。
- Q2は、ウサギの情報だけでなく、怠惰な動物の情報も取得したいと考えています。
ルーティングキーとして「quick.orange.rabbit」を含むメッセージはQ1とQ2の2つのキューにプッシュされ、「lazy.orange.elephant」のルーティングキーを含むメッセージもQ1とQ2にプッシュされます。ただし、ルーティングキーが「quick.orange.fox」の場合、メッセージはQ1にのみプッシュされます。ルーティングキーが「lazy.brown.fox」のメッセージはQ2にプッシュされ、ルーティングキーは「lazy.pink」です。 「rabbit」メッセージもQ2にプッシュされますが、同じメッセージはQ2に1回だけプッシュされます。
メッセージの送信時に指定された交換キーとルーティングキーに対応する交換キーとバインディングキーがコンシューマ側でバインドされていない場合、このメッセージは破棄されます。例:「オレンジ」および「quick.orange.male.rabbit」。ただし、ルーティングメッセージ「lazy.orange.male.rabbit」はQ2にプッシュされます。
トピックタイプの交換:
トピック型の交換は非常に強力で、他のタイプの交換も実現できます。
- キューが「#」のバインディングキーでバインドされると、ファンアウトタイプの交換と非常によく似たすべてのメッセージを受信します。
- バインディングキーに「*」と「#」が含まれていない場合、直接交換とよく似ています。
2.最終的な実現
ログシステムでトピックタイプの交換を使用します。最初に、2つの単語「。」を使用してルーティングキーを準備しました。コードは前のコード、EmitLogTopic.javaに似ています。
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("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//指定一个topic类型的exchange
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//这里拿到routing key
String routingKey = getRouting(argv);
String message = getMessage(argv);
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");
connection.close();
}
//...
}
ReceiveLogsTopic.javaのコード:
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("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//指定一个topic类型的exchange
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
if (argv.length < 1){
System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
System.exit(1);
}
//绑定binding key
for(String bindingKey : argv){
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
}
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
String routingKey = delivery.getEnvelope().getRoutingKey();
System.out.println(" [x] Received '" + routingKey + "':'" + message + "'");
}
}
}
3.まとめ
上記に基づいて、それはルーティングキーとバインディングキーの書き込みを強化するだけです。