2
头图
In order to facilitate better communication, you can pay attention to the public number: Java class representative , every day, waiting for you!

4 Routing

In the last tutorial we created a simple logging system. Log messages can be broadcast to multiple recipients.

In this tutorial, we'll add a new feature to it - making it possible to subscribe to a certain subset (subset) of messages individually. For example, we only write critical errors to disk files (saving only the critical error log to save disk), while still outputting all logs to the terminal.

Bindings

In the previous example, we have created the binding, you may remember the following code:

 channel.queueBind(queueName, EXCHANGE_NAME, "");

A binding is a relationship between an exchange and a queue. It can be simply understood as: this queue is interested in messages from this exchange.

Bindings can have a routingKey parameter. In order to avoid confusion with the parameters of the basic_publish method (Class Representative Note: The routingKey parameter of the Channel#queueBind(String queue, String exchange, String routingKey) method is used to implement the binding relationship, and The routingKey of the Channel#basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) method is used to route messages), we will call it binding key :

 channel.queueBind(queueName, EXCHANGE_NAME, "black");

The meaning of the binding key depends on the type of exchange. The swap of the type we used earlier fanout ignores its value.

Direct exchange

The logging system from our previous tutorial broadcasts all messages to all consumers. We want to be able to filter based on the urgency of the message. For example, you might want an application to only log critical errors to disk and not log warnings and informational log messages to save disk.

The fanout exchange we used before was not very flexible - it was just a no-brainer broadcast.

direct exchange替代, direct exchange简单——消息会进入那个队列的binding key与消息的routing key Exactly matching queue.

To illustrate this point, consider the following setup:

direct exchange X设置中,可以看到---f980a0b2a957d74c736951b0a59913c1---绑定了两个queue . Q1使用---37f7abcfd273ed6de5721ed2e8772c2b orange binding key , Q2 has two bindings, one is black and the other is green .

在该设置下, orange 359f74d422ca3ab3ab658184ee632cf4---作为routing key的消息, exchange后将被路由到Q1 .使用black or green messages as routing key will be routed to Q2 . Other messages will be discarded.

Multiple bindings

Using the same binding key to bind to multiple queue is perfectly legal. In the image above, we bind X and Q1 via black as binding key . In this way, direct exchange will broadcast the message to all matching queues like fanout , (class representative note: fanout is a no-brainer broadcast to all queue , direct are sent to all matching queue ).

Emitting logs

We will apply this model to the logging system. Use direct exchange instead of fanout to send the message. We will provide the log level as routing key . This way, the receiving program can receive messages according to the level he wants. Let's take a look at the sending log first.

As before, first create a exchange :

 channel.exchangeDeclare(EXCHANGE_NAME, "direct");

Then you can send the message:

 channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());

For simplicity, we assume log levels are divided into: 'info', 'warning', 'error'.

Subscribing

The receiver message program works as in the previous tutorial, with one exception, we need to create bindings for the log levels of interest.

 String queueName = channel.queueDeclare().getQueue();

for(String severity : argv){
  channel.queueBind(queueName, EXCHANGE_NAME, severity);
}

Putting it all together

EmitLogDirect.java complete code:

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

public class EmitLogDirect {

  private static final String EXCHANGE_NAME = "direct_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, "direct");

        String severity = getSeverity(argv);
        String message = getMessage(argv);

        channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
    }
  }
  //..
}

ReceiveLogsDirect.java complete code:

 import com.rabbitmq.client.*;

public class ReceiveLogsDirect {

  private static final String EXCHANGE_NAME = "direct_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, "direct");
    String queueName = channel.queueDeclare().getQueue();

    if (argv.length < 1) {
        System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
        System.exit(1);
    }

    for (String severity : argv) {
        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 '" +
            delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
  }
}

Compile as before (refer to section 1 for compilation and classpath settings). For convenience, we use the environment variable $CP (%CP% under Windows) as the classpath when running the example.

 javac -cp $CP ReceiveLogsDirect.java EmitLogDirect.java

If you only want to save 'warning' and 'error' (don't want 'info') level logs to a file, just open a terminal and type:

 java -cp $CP ReceiveLogsDirect warning error > logs_from_rabbit.log

If you want to see all the messages on the screen, open a new terminal and type:

 java -cp $CP ReceiveLogsDirect info warning error
# => [*] Waiting for logs. To exit press CTRL+C

Finally, as an example, send an 'error' level log message:

 java -cp $CP EmitLogDirect error "Run. Run. Or it will explode."
# => [x] Sent 'error':'Run. Run. Or it will explode.'

(See (EmitLogDirect.java source) and (ReceiveLogsDirect.java source) for the complete source code)

Continue to tutorial 5 to learn how to listen for messages based on patterns.


【Recommended reading】

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, and get more Java dry goods in time.


Java课代表
640 声望1k 粉丝

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