4 Routing


官方文档地址: 4 Routing


有选择地接收消息。

前提条件

本教程假设你已经安装了 RabbitMQ 并在本地主机端口(5672)上运行。

路由

在前一篇教程中,我们构建了一个简单的日志系统。我们能够向许多接收者广播日志消息。

在本教程中,我们将为其添加一个特性 - 仅订阅消息的一个子集。例如,我们仅将关键的错误消息定向到日志文件(以节省磁盘空间),同时仍然能够在控制台上打印所有的日志消息。

绑定

在前面的示例中,我们已经创建了绑定,回忆代码如下:

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

绑定是交换器和队列之间的关系。这可以简单地理解为:队列对来自这个交换器的消息感兴趣。

绑定可以使用额外的routingKey参数。为了避免与basic_publish参数混淆,我们将其称为binding key。我们创建一个绑定键:

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

绑定键的作用取决于exchange类型。我们之前使用的fanout交换器会忽略了它的值。

Direct 交换器

之前教程中的日志系统将消息广播给所有用户。我们希望对其进行扩展,允许根据消息的严重程度过滤消息。例如,我们可能希望只将error级别的日志消息写入磁盘,而不会将warninginfo日志消息写入磁盘。

我们使用的是fanout交换器,它没有给我们太多的灵活性 - 只能进行盲目的广播。

我们将用direct交换器代替它。direct交换器背后的路由算法很简单 - 消息将进入绑定键binding key与消息的路由键routing key完全匹配的队列。

为了说明这一点,请考虑以下设置:

在这个设置中,我们可以看到direct交换器X绑定了两个队列。第一个队列用绑定键orange绑定,第二个队列有两个绑定,一个绑定键是black,另一个是green

在这样的设置中,使用路由键orange发布到交换器的消息将被路由到队列Q1。带有blackgreen路由键的消息将转到Q2。其他消息将被丢弃。

多重绑定


使用相同的绑定键绑定多个队列是完全合法的。在我们的示例中,可以在XQ1之间添加一个绑定键black。在这种情况下,direct交换器将像fanout交换器一样工作,将消息广播到所有匹配的队列。带有路由键black的消息将被发送到Q1Q2

发出日志

我们将在日志系统中使用这个模型。我们将发送消息到一个direct交换器,而不是发送到fanout交换器。我们将提供日志严重性作为routing key。这样,接收程序将能够根据严重性有选择的接收日志。让我们首先来看一下发出日志的代码。

和往常一样,我们需要先创建一个交换器:

channel.exchangeDeclare(EXCHANGE_NAME, "direct");

我们已经准备好发送一个信息:

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

为了简化问题,我们假设日志级别severity可以是infowarningerror

订阅

接收消息的工作方式与前面的教程一样,但有一个例外 - 我们将为感兴趣的每个严重性创建一个新的绑定。

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

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

把它们放一起

EmitLogDirect.java类代码:

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 EmitLogDirect {
    
    
    //交换器名称
    private final static String EXCHANGE_NAME = "direct_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.DIRECT);
            //从命令行接受参数
            //日志级别,路由键
            String severity = getSeverity(args);
            //日志消息
            String message = getMessage(args);
            //发布消息到交换器,并设置路由键
            channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
            System.out.println(" [x] Sent '" + message + "'");
        }
    }

    private static String getSeverity(String[] strings) {
    
    
        if (strings.length < 1) {
    
    
            return "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();
    }
}

ReceiveLogsDirect.java类代码:

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author wangbo
 * @date 2019/10/22 18:25
 */
public class ReceiveLogsDirect {
    
    
    //交换器名称
    private final static String EXCHANGE_NAME = "direct_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.DIRECT);
        //获取临时队列的名称
        String queueName = channel.queueDeclare().getQueue();
        //将临时队列和交换器绑定,进行多重绑定
        if (args.length < 1) {
    
    
            System.err.println("Usage: ReceiveLogsDirect [info] [warning] [error]");
            System.exit(1);
        }
        for (String severity : args) {
    
    
            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 '" + message + "'");
        };
        //消费者监听
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
    
    });
    }
}

照常编译(有关编译和类路径建议,请参阅教程1)。为了方便,在运行示例时,我们将对类路径使用环境变量$CP(即Windows上的%CP%)。

javac -cp $CP ReceiveLogsDirect.java EmitLogDirect.java

如果你只想保存warningerror(而不是info)日志信息到一个文件,只要打开控制台并键入:

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

如果你想在你的屏幕上看到所有的日志信息,打开一个新的终端:

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

例如,要发出error日志消息,只需输入:

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

请继续阅读第5课,了解如何根据模式侦听消息。

猜你喜欢

转载自blog.csdn.net/wb1046329430/article/details/115283884
今日推荐