(7)RabbitMQ之direct交换机——routing(路由)

本机将会介绍一个新的exchange:direct交换机,通过程序示例演示其消息routing(路由)的功能。

概述

本机我们将通过一个发送日志的程序来演示direct类型的exchange的routing功能。生产者发送日志,级别分别为info,warn,error等,消费者只会接收routing key 跟其队列的binding key完全匹配的消息。

相关概念介绍

1、Bindings(绑定)

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

在上一篇博客《(6)RabbitMQ之fanout交换机——Publish/Subscribe(发布订阅)》中我们已经用过上面的代码进行绑定。绑定是交换机和队列之间的关系。这可以简单地理解为:队列对来自此交换机的消息感兴趣。绑定可以采用额外的routingKey参数。为了避免与basic_publish参数混淆,我们将其称为 binding key。这就是我们如何使用键创建绑定:

channel.queueBind(queueName,EXCHANGE_NAME,“black”);

绑定键的意义依赖于转发器的类型。对于fanout类型,忽略此参数。

2、Direct exchange(直接转发)

上节博客中使用fanout类型的交换机,只能够对消息进行转发,不够灵活,不能够对消息按照一定的规则进行转发,而direct类型的交换机则可以做到这一点。direct类型的交换机背后的路由转发算法很简单:消息会被推送至binding key和消息routing key完全匹配的队列。图解:

(7)RabbitMQ之direct交换机——routing(路由)_第1张图片

 

上图,我们可以看到direct类型的转发器与两个队列绑定。第一个队列与绑定键orange绑定,第二个队列与转发器间有两个绑定,一个与绑定键black绑定,另一个与green绑定键绑定。

这样的话,当一个消息附带一个选择键(routing key) orange发布至转发器将会被导向到队列Q1。消息附带一个选择键(routing key)black或者green将会被导向到Q2,所有的其他的消息将会被丢弃。

3、multiple bindings(多重绑定)

使用相同的binding key绑定多个队列是完全合法的。在我们的示例中,我们可以在X和Q1之间添加绑定键black绑定。在这种情况下,direct交换机将表现得想fanout交换机一样,并将消息广播到所有匹配的队列。路由键为black的消息将传送到Q1和Q2。

(7)RabbitMQ之direct交换机——routing(路由)_第2张图片

程序示例

我们通过消费者发送info,warn,error的日志,发送的日志级别(也就是routing key)是随机的。消费者有两个,消费者1只接收info级别的日志,消费者2只接收warn,error的日志。

注意:这里消费者1和2是两个不同的队列,消费者2有两个binding key,可以通过多次调用channel.queueBind()来添加多个binding key,如下所示:

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

消息生产者代码如下:项目GitHub地址 https://github.com/RookieMember/RabbitMQ-Learning.git。

package cn.wkp.rabbitmq.newest.exchange.direct;
 
import java.util.Random;

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


import cn.wkp.rabbitmq.util.ConnectionUtil;
 
public class Send {
 
    private final static String EXCHANGE_NAME = "direct_exchange";
    
    private static String[] logLevels={"info","warn","error"};
 
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        // 从连接中创建通道
        Channel channel = connection.createChannel();
 
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
 
        Random random = new Random();
        // 消息内容
        for(int i=0;i<10;i++){
        	//随机得到路由键
	    	String routingKey = logLevels[random.nextInt(3)];
		String message = "日志序号i:"+i+",级别:"+routingKey;
	    	channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
	    	System.out.println("Sent message:" + message);
        }
        //关闭通道和连接
        channel.close();
        connection.close();
    }
}

消费者1代码如下:

package cn.wkp.rabbitmq.newest.exchange.direct;

import java.io.IOException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import cn.wkp.rabbitmq.util.ConnectionUtil;

public class Recv1 {

	private final static String EXCHANGE_NAME = "direct_exchange";
	private final static String QUEUE_NAME = "direct_queue1";
	private final static String BINDING_KEY = "info";

	public static void main(String[] argv) throws Exception {

		// 获取到连接以及mq通道
		Connection connection = ConnectionUtil.getConnection();
		final Channel channel = connection.createChannel();

		// 声明交换机
		channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
		// 声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		// 绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, BINDING_KEY);

		// 指该消费者在接收到队列里的消息但没有返回确认结果之前,它不会将新的消息分发给它。
		channel.basicQos(1);

		// 定义队列的消费者
		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope,
					BasicProperties properties, byte[] body) throws IOException {
				System.out.println("消费者1收到消息:" + new String(body));
				// 消费者手动发送ack应答
				channel.basicAck(envelope.getDeliveryTag(), false);
			}
		};
		// 监听队列
		channel.basicConsume(QUEUE_NAME, false, consumer);
	}
}

消费者2代码如下:

package cn.wkp.rabbitmq.newest.exchange.direct;

import java.io.IOException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import cn.wkp.rabbitmq.util.ConnectionUtil;

public class Recv2 {

	private final static String EXCHANGE_NAME = "direct_exchange";
	private final static String QUEUE_NAME = "direct_queue2";
	private final static String[] BINDING_KEYS = {"warn","error"};

	public static void main(String[] argv) throws Exception {

		// 获取到连接以及mq通道
		Connection connection = ConnectionUtil.getConnection();
		final Channel channel = connection.createChannel();

		// 声明交换机
		channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
		// 声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		// 绑定队列到交换机
		for (String bindingKey : BINDING_KEYS) {
			//循环添加了多个binding key
			channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, bindingKey);
		}

		// 指该消费者在接收到队列里的消息但没有返回确认结果之前,它不会将新的消息分发给它。
		channel.basicQos(1);

		// 定义队列的消费者
		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope,
					BasicProperties properties, byte[] body) throws IOException {
				System.out.println("消费者2收到消息:" + new String(body));
				// 消费者手动发送ack应答
				channel.basicAck(envelope.getDeliveryTag(), false);
			}
		};
		// 监听队列
		channel.basicConsume(QUEUE_NAME, false, consumer);
	}
}

运行结果如下(由于routing key是随机的,所以可能每次结果都不一样):

Sent message:日志序号i:0,级别:warn
Sent message:日志序号i:1,级别:error
Sent message:日志序号i:2,级别:warn
Sent message:日志序号i:3,级别:error
Sent message:日志序号i:4,级别:info
Sent message:日志序号i:5,级别:warn
Sent message:日志序号i:6,级别:info
Sent message:日志序号i:7,级别:error
Sent message:日志序号i:8,级别:warn
Sent message:日志序号i:9,级别:warn
消费者1收到消息:日志序号i:4,级别:info
消费者1收到消息:日志序号i:6,级别:info
消费者2收到消息:日志序号i:0,级别:warn
消费者2收到消息:日志序号i:1,级别:error
消费者2收到消息:日志序号i:2,级别:warn
消费者2收到消息:日志序号i:3,级别:error
消费者2收到消息:日志序号i:5,级别:warn
消费者2收到消息:日志序号i:7,级别:error
消费者2收到消息:日志序号i:8,级别:warn
消费者2收到消息:日志序号i:9,级别:warn

通过控制台输出可以看到已经达到了我们想要的结果,下一节将会介绍另一种topic类型的交换机。

你可能感兴趣的:(RabbitMQ)