一、基本介绍
RabbitMQ是对AMQP(即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准的实现)的一种实现,其传输层是使用的TCP协议。
1、virtual host & users
vrital host
是虚拟主机,而users
用户是需要绑定到对应的虚拟主机的,而发送&接收消息都是面向的对应虚拟主机
。
例如RabbitMQ的管理界面
用户是有对应到virtual host
的,同时可以添加用户对应的virtual host
2、连接模型
上面是画了生产者,将左边的映射到右边就是消费者。
这里我们可以看到连接到RabbitMQ服务器,然后在具体的消息传输的时候是使用的channel
来传输。
二、具体的使用
实现我们可以看下其的官网对其使用的几种模型。首先我们来看下最基本的描叙,及简单的生产消费模型
1、最简单的使用模型
1)、生产者
public class SimpleProducerMain {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:队列是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
/**
* 消息发布
* 参数1:交换机名称(当前没有使用)
* 参数2:队列名称
* 参数3:控制消息的特征(例如这条消息需不需要持久化),当前是设置这条消息需要持久化
* 参数4:消息的内容
*/
connectionChannel.basicPublish("",queue_1, MessageProperties.PERSISTENT_TEXT_PLAIN,"add msg 1".getBytes());
System.out.println("msg send success");
connectionChannel.close();
connection.close();
}
}
然后我们运行在去界面查看:
可以看到目前我们已经有1条准备(Ready
)的消息,0条没有ack
确认(Unacked
)的消息,然后总共是1条消息。
然后我们能看到消息的具体内容
上面的Messages
是能设置获取消息的条数的,例如获取3
条消息,当前是获取1条。
2)、消费者
public class SimpleConsumerMain {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:队列是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
connectionChannel.basicConsume(queue_1,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer Get Msg : " + new String(body));
}
});
Thread.sleep(1000L); //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
connectionChannel.close();
connection.close();
}
}
这个是消费者代码,运行后就会从队列中获取消息打印(这里的basicConsume
是使用了多线程)。
Consumer Get Msg : add msg 1
Consumer Get Msg : add msg 1
Process finished with exit code 0
3)、测试
我们试下我们在生产端&消费端不关闭管道&连接看下管理界面的Connections
可以看到我们目前有两台机连接,然后点击对应的连接就能看到具体的内容
2、工作队列模型
这种模型是多个消费者消费同一个队列。
1)、生产者
public class SimpleProducerMain {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
/**
* 消息发布
* 参数1:交换机名称(当前没有使用)
* 参数2:队列名称
* 参数3:控制消息的特征(例如这条消息需不需要持久化)
* 参数4:消息的内容
*/
for (int i = 0; i < 20; i++) {
connectionChannel.basicPublish("",queue_1, MessageProperties.PERSISTENT_TEXT_PLAIN,("add msg "+i).getBytes());
}
System.out.println("msg send success");
// connectionChannel.close();
// connection.close();
}
}
这里其实我们就是改为循环发送消息。
2)、消费者
public class SimpleConsumerMain1 {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
connectionChannel.basicConsume(queue_1,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 2 Get Msg : " + new String(body));
}
});
Thread.sleep(1000L); //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
// connectionChannel.close();
// connection.close();
}
}
public class SimpleConsumerMain2 {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
connectionChannel.basicConsume(queue_1,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 1 Get Msg : " + new String(body));
}
});
Thread.sleep(1000L); //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
// connectionChannel.close();
// connection.close();
}
}
这两个消费者的代码都是和前面的是一样的,我们是在打印的时候加了标识Consumer 1
&Consumer 2
。
我们启动两个消费者,然后再启动生产者发送消息
Consumer 1 Get Msg : add msg 1
Consumer 1 Get Msg : add msg 3
Consumer 1 Get Msg : add msg 5
Consumer 1 Get Msg : add msg 7
Consumer 1 Get Msg : add msg 9
Consumer 1 Get Msg : add msg 11
Consumer 1 Get Msg : add msg 13
Consumer 1 Get Msg : add msg 15
Consumer 1 Get Msg : add msg 17
Consumer 1 Get Msg : add msg 19
Consumer 2 Get Msg : add msg 0
Consumer 2 Get Msg : add msg 2
Consumer 2 Get Msg : add msg 4
Consumer 2 Get Msg : add msg 6
Consumer 2 Get Msg : add msg 8
Consumer 2 Get Msg : add msg 10
Consumer 2 Get Msg : add msg 12
Consumer 2 Get Msg : add msg 14
Consumer 2 Get Msg : add msg 16
Consumer 2 Get Msg : add msg 18
我们可以看到两个消费者是1个一个交叉顺序获取消息的
3)、拓展1(ack应答确认)
我们前面的代码是消费者自动ack
确认,同时多个消费者平均分配,并且这些消息是一下子全部给了两个消费者,这样可能就会存在问题。例如消费者拿到5个消息后,在处理2个后就宕机了,然后剩下的3个就丢失了,所以我们需要改一下,也就是说我们需要手动在消费者中ACK
确认,同时设置一次给一个消息给消费者,这样就避免了上面的问题了。
我们先演示下消息未确认的情况,生产者我们使用相同的代码,消费者我们只启动一个,再将自动确认取消,同时我们也不手动确认:
public class SimpleConsumerMain2 {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
connectionChannel.basicConsume(queue_1,false,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 1 Get Msg : " + new String(body));
}
});
Thread.sleep(1000L); //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
// connectionChannel.close();
// connection.close();
}
}
我们启动生产者后可以看到目前我们有了20个准备的消费者,我们再启动消费者:
可以看到我们没有确认的消息有20个。 这里我们需要注意,我们没有ack
应答的消息是可以被消费者再次消费的,例如这里我们可以启动没有修改的SimpleConsumerMain1
,其运行的时候就会将这些Unacked
消息全都消费。
4)、拓展2(应答后再给通道另一条消息)
我们修改两个消费者
public class SimpleConsumerMain1 {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
// 设置通道的载入数 设置为1,一次只载入1条
connectionChannel.basicQos(1);
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
connectionChannel.basicConsume(queue_1,false,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 参数2:表示只应答确认这条消息
connectionChannel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("Consumer 2 Get Msg : " + new String(body) + "- Delivery Tag " + envelope.getDeliveryTag());
}
});
Thread.sleep(1000L); //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
// connectionChannel.close();
// connection.close();
}
}
public class SimpleConsumerMain2 {
public final static String queue_1 = "queue_1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
connectionChannel.basicQos(1,false);
/**
* 通道绑定对应的消息队列
* 参数1:队列名称
* 参数2:是否持久化
* 参数3:该队列是否给这个链接独占
* 参数4:消费完成后(不再使用),是否自动删除这个队列
* 参数5:其他参数
*/
connectionChannel.queueDeclare(queue_1,false,false,
false,null);
connectionChannel.basicConsume(queue_1,false,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
connectionChannel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("Consumer 1 Get Msg : " + new String(body) + "- Delivery Tag " + envelope.getDeliveryTag());
}
});
Thread.sleep(1000L); //防止主线程直接关闭,然后在`DefaultConsumer`中不能打印消息
// connectionChannel.close();
// connection.close();
}
}
我们再运行:
Consumer 1 Get Msg : add msg 0- Delivery Tag 1
Consumer 1 Get Msg : add msg 3- Delivery Tag 2
Consumer 1 Get Msg : add msg 5- Delivery Tag 3
Consumer 1 Get Msg : add msg 7- Delivery Tag 4
Consumer 1 Get Msg : add msg 9- Delivery Tag 5
Consumer 1 Get Msg : add msg 11- Delivery Tag 6
Consumer 1 Get Msg : add msg 14- Delivery Tag 7
Consumer 1 Get Msg : add msg 17- Delivery Tag 8
Consumer 1 Get Msg : add msg 19- Delivery Tag 9
Consumer 2 Get Msg : add msg 1- Delivery Tag 1
Consumer 2 Get Msg : add msg 2- Delivery Tag 2
Consumer 2 Get Msg : add msg 4- Delivery Tag 3
Consumer 2 Get Msg : add msg 6- Delivery Tag 4
Consumer 2 Get Msg : add msg 8- Delivery Tag 5
Consumer 2 Get Msg : add msg 10- Delivery Tag 6
Consumer 2 Get Msg : add msg 12- Delivery Tag 7
Consumer 2 Get Msg : add msg 13- Delivery Tag 8
Consumer 2 Get Msg : add msg 15- Delivery Tag 9
Consumer 2 Get Msg : add msg 16- Delivery Tag 10
Consumer 2 Get Msg : add msg 18- Delivery Tag 11
可以看到已经不是交叉顺序获取消息了。
我们再修改一下两个消费者,在里面休眠1秒钟(观察还是不是一次就获取大量消息,还是一个一个获取):
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 参数2:表示只应答确认这条消息
connectionChannel.basicAck(envelope.getDeliveryTag(),false);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Consumer 2 Get Msg : " + new String(body) + "- Delivery Tag " + envelope.getDeliveryTag());
}
然后我们看管理界面,能看到消费的曲线了。
3、广播模型
广播模型是指我们发送一次消息,然后所有的消费者都能获取到这条消息。其过程是生产者生产消息发送给交换机(exchange
),然后交换机面向多个消费者创建多个临时队列传输相同的消息。
1)、生产者
public class ExchangeProducerMain {
public final static String Exchange_Name = "exchange_20210418";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/virtual_20210417");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
//设置交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"fanout");
for (int i = 0; i < 5; i++) {
connectionChannel.basicPublish(Exchange_Name,"",null,("exchange msg " + i).getBytes());
}
System.out.println("Send Msg success");
connectionChannel.close();
connection.close();
}
}
2)、消费者
public class ExchangeConsumerMain1 {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
// 设置通道的载入数 设置为1,一次只载入1条
connectionChannel.basicQos(1);
// 设置交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"fanout");
//获取交换机临时的队列名称
String queueName = connectionChannel.queueDeclare().getQueue();
// 绑定队列、交换机
connectionChannel.queueBind(queueName,Exchange_Name,"");
connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(" ExchangeConsumer 1 Get Msg : " + new String(body));
}
});
// connectionChannel.close();
// connection.close();
}
}
public class ExchangeConsumerMain2 {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
//创建链接所需的内容
factory.setHost("127.0.0.1");
factory.setPort(5672);
//需要注意 用户要配置到这个虚拟主机
factory.setVirtualHost("/virtual_20210417");
factory.setUsername("guest");
factory.setPassword("guest");
//创建链接
Connection connection = factory.newConnection();
//创建通道(可以创建多个通道),因为消息的发送&获取是通过通道
Channel connectionChannel = connection.createChannel();
// 设置通道的载入数 设置为1,一次只载入1条
connectionChannel.basicQos(1);
// 设置交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"fanout");
//获取交换机临时的队列名称
String queueName = connectionChannel.queueDeclare().getQueue();
// 绑定队列、交换机
connectionChannel.queueBind(queueName,Exchange_Name,"");
connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(" ExchangeConsumer 2 Get Msg : " + new String(body));
}
});
// connectionChannel.close();
// connection.close();
}
}
3)、demo说明
然后我们先启动两个消费者:
然后可以看到这里就有创建了两个临时队列
交换机中也有我们新建的交换机了。然后我们运行生产者,看两个消费者的打印信息
ExchangeConsumer 1 Get Msg : exchange msg 0
ExchangeConsumer 1 Get Msg : exchange msg 1
ExchangeConsumer 1 Get Msg : exchange msg 2
ExchangeConsumer 1 Get Msg : exchange msg 3
ExchangeConsumer 1 Get Msg : exchange msg 4
ExchangeConsumer 2 Get Msg : exchange msg 0
ExchangeConsumer 2 Get Msg : exchange msg 1
ExchangeConsumer 2 Get Msg : exchange msg 2
ExchangeConsumer 2 Get Msg : exchange msg 3
ExchangeConsumer 2 Get Msg : exchange msg 4
可以看到两个消费者都获取了全部的消息。
4、静态路由订阅模型
我们在前面的广播模型是面向路由的,只要是这个路由的消费者都能获取到消息(我们没有设置routingKey
的值),而现在我们这个静态路由订阅是设置,满足路由规则才获取到对应的消息,例如两种独立的级别[info]&[error],我们可以设置只获取[info]的消息,也可以[info]&[error]都获取。
1)、生产者
public class DirectProducerMain {
public final static String Virtual_Name = "/virtual_20210417";
public final static String Exchange_Name = "exchange_direct_20210418";
public final static String User_Name = "guest";
public final static String Password = "guest";
public final static String Routing_Error = "error";
public final static String Routing_Info = "info";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost(Virtual_Name);
connectionFactory.setUsername(User_Name);
connectionFactory.setPassword(Password);
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
connectionChannel.exchangeDeclare(Exchange_Name,"direct");
//这里是发送 info 消息
connectionChannel.basicPublish(Exchange_Name,Routing_Info,null," [Info] Msg ".getBytes());
//这里是发送 error 消息
connectionChannel.basicPublish(Exchange_Name,Routing_Error,null," [Error] Msg ".getBytes());
connectionChannel.close();
connection.close();
}
}
2)、消费者
public class DirectConsumerMain1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost(Virtual_Name);
connectionFactory.setUsername(User_Name);
connectionFactory.setPassword(Password);
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
// 参数:交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"direct");
// 临时队列名称
String queueName = connectionChannel.queueDeclare().getQueue();
// 参数:队列名称、路由名称、路由Key 当前是绑定 info
connectionChannel.queueBind(queueName,Exchange_Name,Routing_Info);
connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 1 Get Error : " + new String(body));
}
});
}
}
public class DirectConsumerMain2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost(Virtual_Name);
connectionFactory.setUsername(User_Name);
connectionFactory.setPassword(Password);
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
// 参数:交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"direct");
// 临时队列名称
String queueName = connectionChannel.queueDeclare().getQueue();
// 参数:队列名称、路由名称、路由Key
//绑定 Info 路由
connectionChannel.queueBind(queueName,Exchange_Name,Routing_Info);
//绑定 Error 路由
connectionChannel.queueBind(queueName,Exchange_Name,Routing_Error);
connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 2 Get Error&Info : " + new String(body));
}
});
}
}
3)、demo说明
我们先启动两个消费者:
我们可以看到目前一个临时队列是只绑定了info
、另一个是info
&error
都绑定了。现在我们运行生产者:
Consumer 1 Get Error : [Info] Msg
Consumer 2 Get Error&Info : [Info] Msg
Consumer 2 Get Error&Info : [Error] Msg
可以看到两个消费者,一个只消费了info
,另一个是发送的两个消息都消费了。
5、动态路由订阅模型
前面我们是固定的routingKey
,现在我们是模糊匹配
1)、生产者
public class TopicProducerMain {
public final static String Virtual_Name = "/virtual_20210417";
public final static String Exchange_Name = "exchange_topic_20210418";
public final static String User_Name = "guest";
public final static String Password = "guest";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost(Virtual_Name);
connectionFactory.setUsername(User_Name);
connectionFactory.setPassword(Password);
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
connectionChannel.exchangeDeclare(Exchange_Name,"topic");
connectionChannel.basicPublish(Exchange_Name,"save.user",null," [save.user] Msg ".getBytes());
connectionChannel.basicPublish(Exchange_Name,"save.user.id",null," [save.user.id] Msg ".getBytes());
connectionChannel.basicPublish(Exchange_Name,"delete.user",null," [delete.user] Msg ".getBytes());
connectionChannel.basicPublish(Exchange_Name,"delete.user.id",null," [delete.user] Msg ".getBytes());
connectionChannel.close();
connection.close();
}
}
2)、消费者
public class TopicConsumerMain1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost(Virtual_Name);
connectionFactory.setUsername(User_Name);
connectionFactory.setPassword(Password);
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
// 参数:交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"topic");
// 临时队列名称
String queueName = connectionChannel.queueDeclare().getQueue();
// 参数:队列名称、路由名称、路由Key 这里的一个单词的区分是符号[.],然后[*]表示是任意一个单词
connectionChannel.queueBind(queueName,Exchange_Name,"save.*");
connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 1 Get : " + new String(body));
}
});
}
}
public class TopicConsumerMain2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost(Virtual_Name);
connectionFactory.setUsername(User_Name);
connectionFactory.setPassword(Password);
Connection connection = connectionFactory.newConnection();
Channel connectionChannel = connection.createChannel();
// 参数:交换机名称、交换机类型
connectionChannel.exchangeDeclare(Exchange_Name,"topic");
// 临时队列名称
String queueName = connectionChannel.queueDeclare().getQueue();
// 参数:队列名称、路由名称、路由Key 这里的[#]是表示不管后面有多少个单词
connectionChannel.queueBind(queueName,Exchange_Name,"save.#");
//绑定 Error 路由
connectionChannel.queueBind(queueName,Exchange_Name,"delete.*");
connectionChannel.basicConsume(queueName,true,new DefaultConsumer(connectionChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("Consumer 2 Get : " + new String(body));
}
});
}
}
3)、demo说明
我们先启动两个消费者,再启动生产者看下两个消费者的消费内容
Consumer 1 Get : [save.user] Msg
Consumer 2 Get : [save.user] Msg
Consumer 2 Get : [save.user.id] Msg
Consumer 2 Get : [delete.user] Msg
这里的关键就是我们前面的注释,即*
表示只匹配一个单词、#
表示多个单词。