2

5 Topics

In the previous tutorial, we improved the log system. In order to solve the brainless broadcast of fanout exchange b19a9dab5264c86c7ab83272db11c361---, we replaced it with direct , thus realizing selective log reception.

Although using the direct exchange improves the system, it has limitations - it cannot route messages based on multiple criteria.

In our logging system, we want to subscribe to logs based on both log level and log source. You may know this concept from the syslog unix tool, which routes logs based on their level and device.

This would allow a lot of flexibility - we might want to listen to critical error logs from 'cron' and all logs from 'kern'.

To implement this functionality in a logging system, we need to learn a bit more complicated topic exchange.

Topic exchange

A message sent to a topic exchange cannot use random routing_key - it must be a set of words separated by . . Can be any word, but is usually related to some characteristic of the message. These routing key are all legal: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit". There is no limit to the number of words in routing key , but the maximum length cannot exceed 255 bytes.

The format of binding key is the same. The processing logic of topic exchange direct is similar to that of ---5c5d9433a83990435876f9af6edd418f------by specifying routing key the message sent will be distributed to all matching binding key on the queue.

  • (asterisk)* represents a specific word
  • (pound sign)# represents 0 or more words

The following example can easily explain this:

In the figure, we will send some messages that describe the characteristics of the animal. The message will use routing key containing three words (separated by two dots). The first word describes speed, the second describes color, and the third describes variety: "<speed>.<colour>.<species>" .

创建三个绑定: Q1 "*.orange.*"Q2 "*.*.rabbit" "lazy.#" bingding key .

The bindings above are summarized as follows:

  • Q1 Interested in all yellow animals
  • Q2 Want to subscribe to all rabbits and all lazy animals

routing key "quick.orange.rabbit" will be sent to both queues at the same time,
"lazy.orange.elephant" messages are also sent to both queues.另外, "quick.orange.fox" Q1"lazy.brown.fox"会发给Q2"lazy.pink.rabbit" Q2 once, even if there are two bindings matching Q2 , "quick.brown.fox" not match any binding, so it will be discarded.

What if we break the rules and send a message with one or four words (as routing key ), like “orange” or “quick.orange.male.rabbit” ? Of course, these messages will not match any binding and will be discarded.

However, for "lazy.orange.male.rabbit" , although it has four words, will still match the last bingding (lazy.#) and send to Q2

Topic exchange

Topic exchange is very powerful, it can be made to work like any other exchange

When a queue is bound by binding key "#" - he will receive all messages, ignore routing key - just like fanout exchange

When special characters: "*" and "#" are not present in the binding, topic exchange works like direct exchange

Putting it all together

We will use topic exchange in our logging system. We assume that the routing key of the log consists of two words: "<device>.<level>"

The code is almost the same as the previous tutorial

EmitLogTopic.java source code:

 import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

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");
    try (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("UTF-8"));
        System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");
    }
  }
  //..
}

ReceiveLogsTopic.java source code:

 import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;

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");

    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
        String message = new String(delivery.getBody(), "UTF-8");
        System.out.println(" [x] Received '" +
            delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
  }
}

Compile and run the sample code, remember to introduce the classpath as in tutorial 1 - windows use %CP%.

Compile:

 javac -cp $CP ReceiveLogsTopic.java EmitLogTopic.java

receive log messages

 java -cp $CP ReceiveLogsTopic "#"

Receive all log messages for device "kern"

 java -cp $CP ReceiveLogsTopic "kern.*"

Or only care about "critical" level logs

 java -cp $CP ReceiveLogsTopic "*.critical"

Multiple bindings can be created

 java -cp $CP ReceiveLogsTopic "kern.*" "*.critical"

Use routing key "kern.critical" to send log messages:

 java -cp $CP EmitLogTopic "kern.critical" "A critical kernel error"

Have fun with your program. Note that the code does not set a routing key or binding key, you need to specify it through parameters when playing.

(Complete source code reference: EmitLogTopic.java and ReceiveLogsTopic.java )

The next article, in Tutorial 6 , will introduce how to use round-trip messages to implement Remote Procedure Call (Remote Procedure Call)


【Recommended reading】
RabbitMQ Tutorial 4. Routing
RabbitMQ Tutorial 3. Publish/Subscribe
RabbitMQ Tutorial 2. Work Queue (Work Queue)
RabbitMQ Tutorial 1. "Hello World"
Freemarker Tutorial (1) - Template Development Manual
The downloaded attachment name is always garbled? It's time for you to read the RFC document!
Explain MySQL priority queue in simple terms (the order by limit problem you will definitely step on)


It is not easy to code words, please like, follow and share.
Search: [Java class representative], follow the official account, update every day, and get more Java dry goods in time.


Java课代表
640 声望1k 粉丝

JavaWeb一线开发,5年编程经验