RabbitMQ指南(8)-路由消息

1、路由

之前创建过了一个简单的日志系统。可以将日志信息广播至许多接收者。在本节中,会将日志系统增加一个特性:仅订阅日志消息的一个子集。例如,仅仅将关键的错误日志消息写入日志文件(保存在磁盘),同时还能够将所有的日志消息打印至控制台。

2、绑定

在之前的例子中,已经使用过绑定了,调用代码如下:

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

绑定即交换机和队列之间的一个关系。可以简单理解为:该队列关注此交换机中的消息。绑定方法可以传入一个routingKey参数,为了避免和basic_publish参数混淆,将它叫做绑定关键字(binding key)。下面是如何使用关键字来进行绑定:

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

绑定关键字的意义视交换机类型而定。先前使用的广播交换机(fanout)将会忽略该关键字。

3、Direct交换机

之前的日志系统将所有的消息广播至所有的消费者客户端。我们希望在此基础上根据日志的级别来过滤消息。例如,我们可能仅需要将关键的错误日志写入磁盘中,从而不会在warning或info日志消息上浪费磁盘空间。

广播交换机不能提供复杂的特性,它仅能实现简单的广播机制。下面使用direct交换机来代替广播(fanout)交换机,direct交换机的路由算法相对简单:只有队列的绑定关键字和消息的路由关键字完全匹配时,消息才能够发送至队列。如下图的路由机制:

rabbitmq_8_1

可以看出在此路由机制下,有两个队列绑定在同一个direct类型的交换机上。第一个使用orange关键字进行绑定,第二个队列有两个绑定关键字,black和green。在此机制下,使用路由关键字orange发布的消息将会被发布至队列Q1,使用路由关键字black或green将会至Q2,其它所有的消息将会被丢弃。

4、多重绑定(Multiple bindings)

rabbitmq-8-multiple

多个队列使用相同的绑定关键字是非常合法的,在上图所示的例子中,在例子中,可以为Q1增加一个绑定关键字black绑定至交换机X。在这种情况下,direct交换机就像fanout交换机一样,将会广播消息至所有匹配的队列中。使用路由关键字black的消息将会传送至Q1和Q2。

5、产生日志(Emitting logs)

在日志系统中使用这种模型,使用direct交换机代替fanout交换机来发送消息。使用日志级别做为路由关键字,接收程序可以选择它想要接收的级别的日志。

和以前一样,首先需要先创建交换机:

1 channel.exchangeDeclare(EXCHANGE_NAME, "direct");

发送消息:

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

在上面的代码中,可以假定变量severity可取值为:info、warning、error。

6、订阅消息

订阅消息很简单,仅需要将队列关注的级别绑定至相应的交换机即可:

1 String queueName = channel.queueDeclare().getQueue();
2 channel.queueBind(queueName, EXCHANGE_NAME, severity);

7、代码整合

rabbitmq_8-four

7.1、生产者

接收控制台的输入,每行输入以空格分隔,第一位表示日志级别。详细代码如下:

1 package com.zenfery.example.rabbitmq;
2  
3 import java.io.IOException;
4 import java.util.Scanner;
5  
6 import com.rabbitmq.client.Channel;
7 import com.rabbitmq.client.Connection;
8 import com.rabbitmq.client.ConnectionFactory;
9 import com.rabbitmq.client.ConsumerCancelledException;
10 import com.rabbitmq.client.ShutdownSignalException;
11  
12 //生产者
13 public class EmitLogDirect {
14  
15   private static final String EXCHANGE_NAME = "direct_logs"// 交换机名称
16    
17   public static void main(String[] args) throws IOException
18   , ShutdownSignalException, ConsumerCancelledException
19   , InterruptedException {
20     ConnectionFactory factory = new ConnectionFactory();
21     factory.setHost("localhost");
22     factory.setPort(5672);
23     //创建连接
24     Connection connection = factory.newConnection();
25     Channel channel = connection.createChannel();
26     //定义类型为direct的交换机
27     channel.exchangeDeclare(EXCHANGE_NAME, "direct");
28      
29     //发送消息,接收控制台输入,按行输入,日志级别和日志内容中间使用空格分隔
30     Scanner scanner = new Scanner(System.in);
31     while(scanner.hasNextLine()){
32       String line = scanner.nextLine();
33       String severity = line.split(" ")[0];
34       String message = line;
35       //发送消息
36       channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
37       System.out.println("  >>>发送:["+severity+"] "+message+"");
38     }
39   }
40 }

7.2、消费者

启动的消费者根据传入的日志级别参数列表,来决定监听哪些日志级别的日志,其余的将会被忽略:

1 package com.zenfery.example.rabbitmq;
2  
3 import java.io.IOException;
4  
5 import com.rabbitmq.client.Channel;
6 import com.rabbitmq.client.Connection;
7 import com.rabbitmq.client.ConnectionFactory;
8 import com.rabbitmq.client.ConsumerCancelledException;
9 import com.rabbitmq.client.QueueingConsumer;
10 import com.rabbitmq.client.ShutdownSignalException;
11  
12 //消费者
13 public class ReceiveLogsDirect {
14  
15   private static final String EXCHANGE_NAME = "direct_logs"// 交换机名称
16    
17   public static void main(String[] args) throws IOException
18   , ShutdownSignalException, ConsumerCancelledException
19   , InterruptedException {
20     ConnectionFactory factory = new ConnectionFactory();
21     factory.setHost("localhost");
22     factory.setPort(5672);
23     //创建连接
24     Connection connection = factory.newConnection();
25     Channel channel = connection.createChannel();
26     //定义类型为fanout的交换机
27     channel.exchangeDeclare(EXCHANGE_NAME, "direct");
28      
29     //自动生成队列,根据main方法传入的关注的日志级别并绑定队列至交换机
30     String queueName = channel.queueDeclare().getQueue();
31     for(String severity: args){
32       channel.queueBind(queueName, EXCHANGE_NAME, severity);
33     }
34      
35     //创建消费者对象
36     QueueingConsumer consumer = new QueueingConsumer(channel);
37     channel.basicConsume(queueName, true, consumer);
38      
39     while (true) {
40           QueueingConsumer.Delivery delivery = consumer.nextDelivery();
41           String message = new String(delivery.getBody());
42           System.out.println(" >>>接收消息:" + message);
43     }
44   }
45  
46 }

7.3、执行演示

传入参数“error”,启动第一个消费者。传入参数“error info warning”启动第二个消费者。

启动第一个消费者,依次输入:

1 info logs123.
2 error logs456.
3 warning logs789.
4 other logs890.

第一个消费者输出:

1 >>>接收消息:error logs456.

第二个消费者输出:

1 >>>接收消息:info logs123.
2 >>>接收消息:error logs456.
3 >>>接收消息:warning logs789.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值