欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

程序员文章站 2023-10-31 21:34:58
在上一章中,我们构建了一个简单的日志系统,我们可以把消息广播给很多的消费者。在本章中我们将增加一个特性:我们可以订阅这些信息中的一些信息。例如,我们希望只将error级别的错误存储到硬盘中,同时可以将所有级别(error、info、warning等)的日志都打印在控制台上。 1、绑定(Binding ......

  在上一章中,我们构建了一个简单的日志系统,我们可以把消息广播给很多的消费者。在本章中我们将增加一个特性:我们可以订阅这些信息中的一些信息。例如,我们希望只将error级别的错误存储到硬盘中,同时可以将所有级别(error、info、warning等)的日志都打印在控制台上。

1、绑定(bindings)

  在上一章中,我们已经创建了绑定关系,回顾一下代码:

1 channel.queuebind(queuename, exchange_name, "");

  一个绑定是一个交换器与队列之间的关系。意思是指:这个队列对这个交换器的消息感兴趣。

  该方法同时还有另一个routing key参数,为了避免与basic_public参数产生中的路由键(routing key)混淆,我们称之为绑定键(bingind key),下面展示了如何通过一个绑定key创建一个绑定:

1 channel.queuebind(queuename, exchange_name, "black");

  注意,这个绑定键(这里是"black")的含义依赖于交换器的类型。比如在我们的日志系统中,交换器类型为fanout,此时,绑定键没有任何意义,会被忽略掉。

2、直连交换机(direct exchange)

  在我们之前的日志系统中,所有的消息被广播给所有的消费者,但是本章的需要是希望有一个程序可以只接收error级别的日志并保存到磁盘中,而不用浪费空间去存储那些info、warning级别的日志。

  我们正在用的广播模式的交换器并不够灵活,它只是不加思索地进行广播。因此,需要使用direct exchange来代替。直连交换器的路由算法非常简单:将消息推送到binding key与该消息的routing key相同的队列。

  为了说明这点,请看下图:

  RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

  在该图中,直连交换器x上绑定了两个队列。第一个队列绑定了绑定键orange,第二个队列有两个绑定键:black和green。在这种场景下,一个消息在布时指定了路由键为orange将会只被路由到队列q1,路由键为black和green的消息都将被路由到队列q2。其他的消息都将被丢失。

3、多重绑定

  RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

  同一个绑定键可以绑定到不同的队列上去,在上图中,我们也可以增加一个交换器x与队列q2的绑定键,在这种情况下,直连交换器将会和广播交换器有着相同的行为,将消息推送到所有匹配的队列。一个路由键为black的消息将会同时被推送到队列q1和q2。

4、发送日志

  首先我们要一如既往地创建一个交换器:

1 channel.exchangedeclare(exchange_name, "direct");

  并准备发送消息:

1 channel.basicpublish(exchange_name, severity, null, message.getbytes());

  我们需要确保在我们日志系统中参数"severity"是“info”、“warning”和“error”中的一个。

5、订阅

  创建接收消息与上一章基本相同,唯一不同的是,需要在创建绑定关系时,指定severity的值:

1 string queuename = channel.queuedeclare().getqueue();
2 
3 for(string severity : argv){
4   channel.queuebind(queuename, exchange_name, severity);
5 }

6、完整的代码

RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

  emitlogdirect.java

 1 import com.rabbitmq.client.channel;
 2 import com.rabbitmq.client.connection;
 3 import com.rabbitmq.client.connectionfactory;
 4 
 5 public class emitlogdirect {
 6 
 7   private static final string exchange_name = "direct_logs";
 8 
 9   public static void main(string[] argv) throws exception {
10     connectionfactory factory = new connectionfactory();
11     factory.sethost("localhost");
12     try (connection connection = factory.newconnection();
13          channel channel = connection.createchannel()) {
14         channel.exchangedeclare(exchange_name, "direct");
15 
16         string severity = getseverity(argv);
17         string message = getmessage(argv);
18 
19         channel.basicpublish(exchange_name, severity, null, message.getbytes("utf-8"));
20         system.out.println(" [x] sent '" + severity + "':'" + message + "'");
21     }
22   }
23   //..
24 }

  receivelogsdirect.java

 1 import com.rabbitmq.client.*;
 2 
 3 public class receivelogsdirect {
 4 
 5   private static final string exchange_name = "direct_logs";
 6 
 7   public static void main(string[] argv) throws exception {
 8     connectionfactory factory = new connectionfactory();
 9     factory.sethost("localhost");
10     connection connection = factory.newconnection();
11     channel channel = connection.createchannel();
12 
13     channel.exchangedeclare(exchange_name, "direct");
14     string queuename = channel.queuedeclare().getqueue();
15 
16     if (argv.length < 1) {
17         system.err.println("usage: receivelogsdirect [info] [warning] [error]");
18         system.exit(1);
19     }
20 
21     for (string severity : argv) {
22         channel.queuebind(queuename, exchange_name, severity);
23     }
24     system.out.println(" [*] waiting for messages. to exit press ctrl+c");
25 
26     delivercallback delivercallback = (consumertag, delivery) -> {
27         string message = new string(delivery.getbody(), "utf-8");
28         system.out.println(" [x] received '" +
29             delivery.getenvelope().getroutingkey() + "':'" + message + "'");
30     };
31     channel.basicconsume(queuename, true, delivercallback, consumertag -> { });
32   }
33 }

  为了测试方便,我们可以把"info"、"error"、"warning"都绑定到一个队列上去,然后生产者分别往"info"、"error"、"warning"发送消息:

RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

  此时查看rabbitmq控制台:

  RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

  到此,发布-订阅涉及到的相关知识点都讲解完了,下一章将讲解topic(主题模式)。