Article Directory
Official document address: 5 Topics
Receive messages based on patterns (topics).
Prerequisites
This tutorial assumes that you have installed RabbitMQ and is running on the local host port (5672).
theme
In the previous tutorial, we improved the logging system. We use a direct
switch instead of a switch that can only perform analog broadcasts fanout
, thereby gaining the possibility of selectively receiving logs.
Although the use of direct
switches has improved our system, it still has limitations-it cannot be routed based on multiple conditions.
In our logging system, we may not only want to subscribe to logs based on severity, but also want to subscribe to logs based on the source that issued the log. You may syslog
know this concept from the unix tool , which routes logs according to severity ( info/warn/crit…
) and device ( auth/cron/kern…
).
This will give us a lot of flexibility-we may want to monitor not only "cron"
the error logs from but also "kern"
all the logs from.
To achieve this in the logging system, we need to understand more complex topic
switches.
Topic Exchange
The message sent to the topic
exchange routing_key
must be a list of words separated by dots. Words can be anything, but usually they specify some characteristics related to the message. Several exemplary efficient routing stock.usd.nyse
keys: nyse.vmw
, quick.orange.rabbit
, . The maximum number 255
of bytes for the routing key .
The binding key must also be in the same form. topic
The logic behind the direct
switch is similar to that of the switch-messages sent using a specific routing key will be delivered to all queues bound with the matching binding key. However, there are two special cases for binding keys:
*
Can replace a word.#
Can replace zero or more words.
To explain the simplest with an example:
In this example, we will send a message describing an animal. These messages will be sent using a routing key consisting of three words (two dots). The first word in the routing key will describe the speed, the second is the color, and the third is the species:<speed>.<colour>.<species>
We created three bindings: binding Q1
with binding key, *.orange.*
binding Q2
with binding key *.*.rabbit
and lazy.#
binding.
These bindings can be summarized as:
Q1
Interested in all orange animals.Q2
Interested in rabbits and slow animals.
Set the routing key to the following content, and the corresponding sending queue is as follows:
"quick.orange.rabbit"
The message with the routing key set to will be sent to two queues."lazy.orange.elephant"
Messages with the routing key set to are also sent to these two queues."quick.orange.fox"
Messages with the routing key set to only go to the first queue."lazy.brown.fox"
Messages with the routing key set to only go to the second queue."lazy.pink.rabbit"
The message with the routing key set to will only be sent to the second queue once, even if it matches two bindings."quick.brown.fox"
The message with the routing key set to does not match any bindings and will therefore be discarded.
If we do not abide by the agreement, with one or four words to convey information, such as "orange"
or "quick.orange.mally.rabbit"
, these messages will not match any binding, will be lost.
"lazy.orange.male.rabbit"
On the other hand, even if it has four words, it will match the last binding and will be passed to the second queue.
Topic Exchange
topic
Exchange is powerful, just like other exchanges.
When the queue is#
bound with a binding key, it will receive all messages, regardless of the routing key, just like in anfanout
exchange.
If the binding key does not use special characters*
and#
,topic
it is thedirect
same.
Put them together
We will use the topic
switch in the log system . We first assume that the log routing key has two words: <facility>.<severity>
.
The code is almost the same as the previous tutorial. EmitLogTopic.java
Class code:
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 EmitLogTopic {
//交换器名称
private final static String EXCHANGE_NAME = "topic_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.TOPIC);
//从命令行接受参数
//路由键
String routingKey = getRouting(args);
//日志消息
String message = getMessage(args);
//发布消息到交换器,并设置路由键
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + 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) {
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();
}
}
ReceiveLogsTopic.java
Class code:
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author wangbo
* @date 2019/10/22 18:25
*/
public class ReceiveLogsTopic {
//交换器名称
private final static String EXCHANGE_NAME = "topic_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.TOPIC);
//获取临时队列的名称
String queueName = channel.queueDeclare().getQueue();
//将临时队列和交换器绑定,进行多重绑定
if (args.length < 1) {
System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
System.exit(1);
}
for (String bindingKey : args) {
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 '" + message + "'");
};
//消费者监听
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
});
}
}
Compile and run the example, including 1
the classpath from the tutorial -for use on Windows %CP%
.
javac -cp $CP ReceiveLogsTopic.java EmitLogTopic.java
Receive all logs:
java -cp $CP ReceiveLogsTopic "#"
Receive all kern
logs from the device :
java -cp $CP ReceiveLogsTopic "kern.*"
Or to receive critical
logs of all levels:
java -cp $CP ReceiveLogsTopic "*.critical"
You can create multiple bindings:
java -cp $CP ReceiveLogsTopic "kern.*" "*.critical"
Issue kern.critical
the log with the routing key :
java -cp $CP EmitLogTopic "kern.critical" "A critical kernel error"
Next, 6
learn how to perform round-trip messages as a remote procedure call in the tutorial .