1.简介
RabbitMQ中的交换机有Direct Exchange(直连交换机)、Topic Exchange(通配符交换机)、Fanout Exchange(广播式交换机)、Headers Exchange(Headers交换机)四种,常用的就前三种。
2.Fanout Exchange(广播式交换机)
模型图:这种模式类似于广播的方式,所有发送到Fanout Exchange
交换机上的消息,都会被发送到绑定到该交换机上面的所有队列上,这样绑定到这些队列的消费者就可以接收到该消息。
这种模式不需要指定Routing key路由键,一个交换机可以绑定多个队列queue,一个queue可同时与多个exchange交换机进行绑定;
如果消息发送到交换机上,但是这个交换机上面没有绑定的队列,那么这些消息将会被丢弃;
【生产者】
package com.bruceliu.producer002;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeoutException;
/**
* @author bruceliu
* @create 2019-10-26 11:43
* @description
* 说明:
* 1. 广播式交换机发送的消息的时候不需要指定routing key路由键;
* 2. 所有发送到广播式交换机上面的消息都会被发送到与之绑定的所有队列上;
*/
public class ProducerDemo001 {
private static final String EXCHANGE_NAME = "fanout_exchange_test001";
//广播式交换机
private static final String EXCHANGE_TYPE = "fanout";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//从连接中获取Channel通道对象
Channel channel = null;
try {
channel = connection.createChannel();
//创建交换机对象
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
//发送消息到交换机exchange上
String msg = "hello fanout exchange!!"+new Date();
channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != channel) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != connection) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【消费者1】
package com.bruceliu.consumer002;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 11:49
* @description
*/
public class ConsumerDemo001 {
private static final String EXCHANGE_NAME = "fanout_exchange_test001";
private static final String QUEUE_NAME = "fanout_exchange_queue001";
public static void main(String[] args) {
//获取MQ连接对象
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
try {
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//创建消息通道对象
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机上
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//创建消费者对象
DefaultConsumer consumer = new DefaultConsumer(channel) {
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消息消费者获取消息
String message = new String(body,"UTF-8");
System.out.println("接收到的消息是:"+message);
}
};
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
【消费者2】
package com.bruceliu.consumer002;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 11:49
* @description
*/
public class ConsumerDemo001 {
private static final String EXCHANGE_NAME = "fanout_exchange_test001";
private static final String QUEUE_NAME = "fanout_exchange_queue002";
public static void main(String[] args) {
//获取MQ连接对象
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
try {
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//创建消息通道对象
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机上
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//创建消费者对象
DefaultConsumer consumer = new DefaultConsumer(channel) {
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消息消费者获取消息
String message = new String(body,"UTF-8");
System.out.println("接收到的消息是:"+message);
}
};
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
【运行结果】
因为消费者1和消费者2分别绑定了队列fanout_exchange_queue001和fanout_exchange_queue002,而且这两个队列都给绑定到了交换机fanout_exchange上面,所有两个消费者都能够接收到此消息。
MQ管理控制台交换机绑定消息
3.Direct Exchange(直连交换机)
模型图:【说明】
任何发送到Direct Exchange的消息都会被转发到指定RouteKey中指定的队列Queue;
生产者生产消息的时候需要执行Routing Key路由键;
队列绑定交换机的时候需要指定Binding Key,只有路由键与绑定键相同的话,才能将消息发送到绑定这个队列的消费者;
如果vhost中不存在RouteKey中指定的队列名,则该消息会被丢弃;
【生产者】
package com.bruceliu.producer002;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.Date;
/**
* @author bruceliu
* @create 2019-10-26 12:51
* @description * 说明:
* * 1. 任何发送到Direct Exchange的消息都会被转发到指定RouteKey中指定的队列Queue;
* * 2. 生产者生产消息的时候需要执行Routing Key路由键;
* * 3. 队列绑定交换机的时候需要指定Binding Key,只有路由键与绑定键相同的话,才能将消息发送到绑定这个队列的消费者;
* * 4. 如果vhost中不存在RouteKey中指定的队列名,则该消息会被丢弃;
*/
public class ProducerDemo002 {
private static final String EXCHANGE_NAME = "direct_exchange";
//交换机类型:direct
private static final String EXCHANGE_TYPE = "direct";
//路由键
private static final String EXCHANGE_ROUTE_KEY = "user.add";
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = null;
//从连接中获取Channel通道对象
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
//创建交换机对象
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
//发送消息到交换机exchange上
String msg = "hello direct exchange!!!!"+new Date();
//指定routing key为info
channel.basicPublish(EXCHANGE_NAME, EXCHANGE_ROUTE_KEY, null, msg.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != channel) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != connection) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【消费者1】
package com.bruceliu.consumer002;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 12:54
* @description
*/
public class ConsumerDemo002 {
private static final String QUEUE_NAME = "direct_exchange_queue01";
private static final String EXCHANGE_NAME = "direct_exchange";
//binding key
private static final String EXCHANGE_ROUTE_KEY = "user.delete";
public static void main(String[] args) {
//获取MQ连接对象
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
try {
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//创建消息通道对象
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机上,并且指定routing_key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);
channel.basicQos(1);
//创建消费者对象
DefaultConsumer consumer = new DefaultConsumer(channel) {
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消息消费者获取消息
String message = new String(body, "UTF-8");
System.out.println("【Consumer01】receive message: " + message);
}
};
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
【消费者2】
package com.bruceliu.consumer002;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 12:58
* @description
*/
public class ConsumerDemo002_2 {
private static final String QUEUE_NAME = "direct_exchange_queue02";
private static final String EXCHANGE_NAME = "direct_exchange";
//binding key
private static final String EXCHANGE_ROUTE_KEY01 = "user.add";
private static final String EXCHANGE_ROUTE_KEY02 = "user.delete";
public static void main(String[] args) {
//获取MQ连接对象
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
try {
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//创建消息通道对象
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机上,并且指定routing_key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY01);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY02);
//创建消费者对象
DefaultConsumer consumer = new DefaultConsumer(channel) {
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消息消费者获取消息
String message = new String(body, "UTF-8");
System.out.println("【Consumer02】receive message: " + message);
}
};
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
【运行结果】
因为生产者生产消息指定的路由键为user.add,而且消费者1绑定的队列direct_exchange_queue01对应的绑定键为user.delete,显然消费者1接收不了该消息,而消费者2指定的绑定键为user.add和user.delete,显然消费者就能成功消费此消息。
MQ管理控制台交换机绑定消息
4.Topic Exchange(通配符交换机)
模型图:说明:
任何发送到Topic Exchange的消息都会被转发到所有满足Route Key与Binding Key模糊匹配的队列Queue上;
生产者发送消息的时候需要指定Route Key,同时绑定Exchange与Queue的时候也需要指定Binding Key;
#” 表示0个或多个关键字,“*”表示匹配一个关键字;
如果Exchange没有发现能够与RouteKey模糊匹配的队列Queue,则会抛弃此消息;
如果Binding中的Routing key *,#都没有,则路由键跟绑定键相等的时候才转发消息,类似Direct Exchange;如果Binding中的Routing key为#或者#.#,则全部转发,类似Fanout Exchange;
【生产者】
package com.bruceliu.producer002;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 20:31
* @description
* * 说明:
* * 1. 任何发送到Topic Exchange的消息都会被转发到所有满足Route Key与Binding Key模糊匹配的队列Queue上;
* * 2. 生产者发送消息的时候需要指定Route Key,同时绑定Exchange与Queue的时候也需要指定Binding Key;
* * 3. #” 表示0个或多个关键字,“*”表示匹配一个关键字;
* * 4. 如果Exchange没有发现能够与RouteKey模糊匹配的队列Queue,则会抛弃此消息;
*/
public class ProducerDemo003 {
private static final String EXCHANGE_NAME = "topic_exchange";
//交换机类型:direct
private static final String EXCHANGE_TYPE = "topic";
//路由键
private static final String EXCHANGE_ROUTE_KEY = "user.add.submit";
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = null;
//从连接中获取Channel通道对象
Channel channel = null;
try {
connection = factory.newConnection();
channel = connection.createChannel();
//创建交换机对象
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
//发送消息到交换机exchange上
String msg = "hello topic exchange!!!";
//指定routing key为info
channel.basicPublish(EXCHANGE_NAME, EXCHANGE_ROUTE_KEY, null, msg.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != channel) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (null != connection) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【消费者1】
package com.bruceliu.consumer002;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 20:36
* @description
*/
public class ConsumerDemo003 {
private static final String QUEUE_NAME = "direct_exchange_queue01";
private static final String EXCHANGE_NAME = "topic_exchange";
//binding key
private static final String EXCHANGE_ROUTE_KEY = "user.#";
public static void main(String[] args) {
//获取MQ连接对象
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
try {
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//创建消息通道对象
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机上,并且指定routing_key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);
channel.basicQos(1);
//创建消费者对象
DefaultConsumer consumer = new DefaultConsumer(channel) {
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消息消费者获取消息
String message = new String(body, "UTF-8");
System.out.println("【Consumer01】receive message: " + message);
}
};
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
【消费者2】
package com.bruceliu.consumer002;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author bruceliu
* @create 2019-10-26 20:38
* @description
*/
public class ConsumerDemo003_1 {
private static final String QUEUE_NAME = "direct_exchange_queue02";
private static final String EXCHANGE_NAME = "topic_exchange";
//binding key
private static final String EXCHANGE_ROUTE_KEY01 = "user.*";
public static void main(String[] args) {
//获取MQ连接对象
//获取MQ连接对象
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
//rabbitmq默认虚拟机名称为“/”,虚拟机相当于一个独立的mq服务器
factory.setVirtualHost("/");
try {
// 创建与RabbitMQ服务的TCP连接 获取MQ连接
Connection connection = factory.newConnection();
//创建消息通道对象
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机上,并且指定routing_key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY01);
//创建消费者对象
DefaultConsumer consumer = new DefaultConsumer(channel) {
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消息消费者获取消息
String message = new String(body, "UTF-8");
System.out.println("【Consumer02】receive message: " + message);
}
};
//监听消息队列
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
【运行结果】
因为生产者发送消息的时候指定了Routing Key为user.add.submit, 而消费者1所在的队列Binding Key为user.#, #能够匹配一个或者多个,所有消费者1能够消费此消息; 但是消费者2指定的Binding Key为user.*, *只能匹配一个,所有并不能够匹配到user.add.submit这个路由键,所以消费者2不能消费此消息。
MQ管理控制台交换机绑定消息
5.总结
需要注意的是,RabbitMQ中还有一个默认交换机(Default Exchange):
【a】:默认的Exchange不能进行绑定操作;
【b】:任何发送到默认交换机的消息都会被转发到路由键Routing key和队列queue名字相同的Queue中;
【c】:如果vhost中不存在Routing key中指定的队列名,则该消息会被抛弃;
【d】:该种方式类似于fanout exchange广播式交换机;