(using the Java client)
Prerequisites
This tutorial assumes RabbitMQ is installed and running on localhost on standard port (5672). In case you use a different host, port or credentials, connections settings would require adjusting.
Where to get help
If you’re having trouble going through this tutorial you can contact us through the mailing list.
In the previous tutorial we improved our logging system. Instead of using a fanout exchange only capable of dummy broadcasting, we used a direct one, and gained a possibility of selectively receiving the logs.
在上一个教程中,我们改进了日志系统。我们使用的是direct交换器,而不是使用只能进行虚拟广播的fanout扇出交换器,并且有可能选择性地接收日志。
Although using the direct exchange improved our system, it still has limitations - it can’t do routing based on multiple criteria.
虽然使用 direct交换器 改进了我们的系统,但它仍然有局限性 - 它不能基于多个标准进行路由。
In our logging system we might want to subscribe to not only logs based on severity, but also based on the source which emitted the log. You might know this concept from the syslog unix tool, which routes logs based on both severity (info/warn/crit…) and facility (auth/cron/kern…).
在我们的日志系统中,我们可能不仅要根据严重性订阅日志,还要根据发出日志的源来订阅日志。您可能从syslog unix工具中了解了这个概念,该工具根据严重性(info/warn/crit…)和功能(auth/cron/kern…)来路由日志。
That would give us a lot of flexibility - we may want to listen to just critical errors coming from ‘cron’ but also all logs from ‘kern’.
这将给我们带来很大的灵活性——我们可能只希望听到来自“cron”的关键错误,但也希望听到来自“kern”的所有日志。。
To implement that in our logging system we need to learn about a more complex topic exchange.
要在我们的日志系统中实现这一点,我们需要了解更复杂的 topic主题交换器。
Messages sent to a topic exchange can’t have an arbitrary routing_key - it must be a list of words, delimited by dots. The words can be anything, but usually they specify some features connected to the message. A few valid routing key examples: “stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”. There can be as many words in the routing key as you like, up to the limit of 255 bytes.
发送到topic 交换器的消息不能具有任意 routing_key - 它必须是由点分隔的单词列表
。单词可以是任何内容,但通常它们指定与消息相关的一些功能。一些有效的路由密钥示例:“ stock.usd.nyse ”,“ nyse.vmw ”,“ quick.orange.rabbit ”。路由密钥中可以包含任意数量的单词,最多可达255个字节。
The binding key must also be in the same form. The logic behind the topic exchange is similar to a direct one - a message sent with a particular routing key will be delivered to all the queues that are bound with a matching binding key. However there are two important special cases for binding keys:
绑定密钥也必须采用相同的形式。topic交换器背后的逻辑 类似于direct交换器- 使用特定路由密钥发送的消息将被传递到与匹配绑定密钥绑定的所有队列。但是,绑定键有两个重要的特殊情况:
In this example, we’re going to send messages which all describe animals. The messages will be sent with a routing key that consists of three words (two dots). The first word in the routing key will describe speed, second a colour and third a species: “..”.
在这个例子中,我们将发送所有描述动物的消息。消息将与包含三个单词(两个点)的路由键一起发送。路由键中的第一个单词将描述速度,第二个是颜色,第三个是物种:“ .. ”。
We created three bindings: Q1 is bound with binding key “.orange.” and Q2 with “..rabbit” and “lazy.#”.
我们创建了三个绑定:Q1绑定了绑定键“ .orange. ”,Q2 绑定了“ ..rabbit ”和“lazy.#”。
These bindings can be summarised as:
这些绑定可以概括为:
Q1 is interested in all the orange animals.
Q1对所有橙色动物感兴趣。
Q2 wants to hear everything about rabbits, and everything about lazy animals.
Q2希望听到关于兔子的一切,以及关于懒惰动物的一切。
A message with a routing key set to “quick.orange.rabbit” will be delivered to both queues. Message “lazy.orange.elephant” also will go to both of them. On the other hand “quick.orange.fox” will only go to the first queue, and “lazy.brown.fox” only to the second. “lazy.pink.rabbit” will be delivered to the second queue only once, even though it matches two bindings. “quick.brown.fox” doesn’t match any binding so it will be discarded.
路由密钥设置为“ quick.orange.rabbit ”的消息将传递到两个队列。消息“ lazy.orange.elephant ”也将同时发送给他们。另一方面,“ quick.orange.fox ”只会进入第一个队列,而“ lazy.brown.fox ”只会进入第二个队列。“ lazy.pink.rabbit ”将仅传递到第二个队列一次,即使它匹配两个绑定。“ quick.brown.fox ”与任何绑定都不匹配,因此它将被丢弃。
What happens if we break our contract and send a message with one or four words, like “orange” or “quick.orange.male.rabbit”? Well, these messages won’t match any bindings and will be lost.
如果我们违反合同并发送带有一个或四个单词的消息,例如“ orange ”或“ quick.orange.male.rabbit ”,会发生什么?好吧,这些消息将不匹配任何绑定,将丢失。
On the other hand “lazy.orange.male.rabbit”, even though it has four words, will match the last binding and will be delivered to the second queue.
另一方面,“ lazy.orange.male.rabbit ”,即使它有四个单词,也会匹配最后一个绑定,并将被传递到第二个队列。
Topic exchange
Topic exchange is powerful and can behave like other exchanges.
topic主题交换功能强大,可以像其他交换器一样。
When a queue is bound with “#” (hash) binding key - it will receive all the messages, regardless of the routing key - like in fanout exchange.
当队列与“ # ”(哈希)绑定密钥绑定时 - 它将接收所有消息,而不管路由密钥 - 如fanout 扇出交换器。
When special characters “*” (star) and “#” (hash) aren’t used in bindings, the topic exchange will behave just like a direct one.
当特殊字符“ * ”(星号)和“ # ”(哈希)未在绑定中使用时,topic主题交换器的行为就像direct直接交换器一样.
We’re going to use a topic exchange in our logging system. We’ll start off with a working assumption that the routing keys of logs will have two words: “.”.
我们将在日志记录系统中使用主题交换。我们将首先假设日志的路由键有两个词:“ . ”。
The code is almost the same as in the previous tutorial.
代码与上一个教程中的代码几乎相同 。
The code for EmitLogTopic.java:
import com.rabbitmq.client.*;
import java.io.IOException;
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();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
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();
}
//...
}
The code for ReceiveLogsTopic.java:
import com.rabbitmq.client.*;
import java.io.IOException;
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();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
if (argv.length < 1) {
System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
System.exit(1);
}
for (String bindingKey : argv) {
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
}
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
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] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
Compile and run the examples, including the classpath as in Tutorial 1 - on Windows, use %CP%.
编译并运行示例,包括类路径,如教程1 - 在Windows上,使用%CP%。
To compile 编译:
javac -cp $CP ReceiveLogsTopic.java EmitLogTopic.java
To receive all the logs 要接收所有日志:
java -cp $CP ReceiveLogsTopic "#"
To receive all logs from the facility “kern” 要从设施“ kern ” 接收所有日志:
java -cp $CP ReceiveLogsTopic "kern.*"
Or if you want to hear only about “critical” logs 或者,如果您只想听到“ 关键 ”日志:
java -cp $CP ReceiveLogsTopic "*.critical"
You can create multiple bindings 您可以创建多个绑定:
java -cp $CP ReceiveLogsTopic "kern.*" "*.critical"
And to emit a log with a routing key “kern.critical” type 并使用路由键“ kern.critical ”类型发出日志:
java -cp $CP EmitLogTopic "kern.critical" "A critical kernel error"
Have fun playing with these programs. Note that the code doesn’t make any assumption about the routing or binding keys, you may want to play with more than two routing key parameters.
玩这些程序玩得开心。请注意,代码不会对路由或绑定密钥做出任何假设,您可能希望使用两个以上的路由密钥参数。
(Full source code for EmitLogTopic.java and ReceiveLogsTopic.java)
Next, find out how to do a round trip message as a remote procedure call in tutorial 6
接下来,在教程6中了解如何将往返消息作为远程过程调用.
Production [Non-]Suitability Disclaimer
Please keep in mind that this and other tutorials are, well, tutorials. They demonstrate one new concept at a time and may intentionally oversimplify some things and leave out others. For example topics such as connection management, error handling, connection recovery, concurrency and metric collection are largely omitted for the sake of brevity. Such simplified code should not be considered production ready.
Please take a look at the rest of the documentation before going live with your app. We particularly recommend the following guides: Publisher Confirms and Consumer Acknowledgements, Production Checklist and Monitoring.
Getting Help and Providing Feedback
If you have questions about the contents of this tutorial or any other topic related to RabbitMQ, don’t hesitate to ask them on the RabbitMQ mailing list.
Help Us Improve the Docs ❤️
If you’d like to contribute an improvement to the site, its source is available on GitHub. Simply fork the repository and submit a pull request. Thank you!