本篇博客接着上一篇介绍MQ的队列模式。
订阅模式
P:生产者
c1、c2:消费者
红色:消息队列
x:交换机
这种模式猛地一看和Work模式很像,但是这个模式中的每个消费者都有自己的队列,同时引入了一个新的概念——交换机。
交互机分别绑定生产者和消息队列,生产者发送的消息会经过交换机到达队列,这样可以实现一个消息被多个消费者获取的目的。
注意:
如果交换机没有绑定队列,生产者发送的消息会丢失,因为交互机没有存储消息的能力,消息只能存在队列之中。
生产者代码:
消费者一代码:
消费者二代码:
以上消费者一和消费者二种分别声明了自己的队列,都绑定到了同一个在生成者端声明的交换机中。
生产者发送一条消息到交换机,绑定到了交换机的队列都可以获得这条消息,实现一条消息被多个消费者获得的功能。
在RabbitMQ的管理工具中可以查看绑定到交换机的队列,如下:
路由模式
路由模式中的元素和订阅者模式中的元素一样,但是路由模式是在订阅者模式基础上的升级。订阅者模式中的队列绑定了交换机后会接受生成者发送的所有消息,但是消费者可能并不想接受所有的消息,路由模式可以实现消息的订制。
路由模式在生产者发送消息时会声明一个key,消费者的队列在绑定交换机是会表明自己接受什么key值的消息,下面来看一下代码:
消费者代码:
生产者发送了两条消息,一条key为delete,另一条key为update。在消费者端,队列绑定交互机时声明了delete这个key,这样当前消费者只会接受到delete消息,不会接收到奥update消息。
通配符模式
#:可以匹配一个或多个词
*:只能匹配一个词
例:
item.category.add,使用#写为item.#即可匹配到,使用*则需写为item.*.*
生产者代码:
消费者代码:
消费者端的的队列在绑定交换机时只需要写item.#即可匹配到item.delete和item.update。
小结
通配模式——Topic
订阅模式
c1、c2:消费者
红色:消息队列
x:交换机
这种模式猛地一看和Work模式很像,但是这个模式中的每个消费者都有自己的队列,同时引入了一个新的概念——交换机。
交互机分别绑定生产者和消息队列,生产者发送的消息会经过交换机到达队列,这样可以实现一个消息被多个消费者获取的目的。
注意:
如果交换机没有绑定队列,生产者发送的消息会丢失,因为交互机没有存储消息的能力,消息只能存在队列之中。
生产者代码:
public class Send {
private final static String EXCHANGE_NAME = "test_exchange_fanout";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明交换机(exchange)
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
String message = "Hello World"; // 消息内容
// 将消息发送到交换机
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
消费者一代码:
public class Recv {
private final static String QUEUE_NAME = "test_queue_fanout_1";
private final static String EXCHANGE_NAME = "test_exchange_fanout";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
// 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 前台系统: '" + message + "'");
Thread.sleep(10);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
消费者二代码:
public class Recv2 {
private final static String QUEUE_NAME = "test_queue_fanout_2";
private final static String EXCHANGE_NAME = "test_exchange_fanout";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
// 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 搜索系统: '" + message + "'");
Thread.sleep(10);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
以上消费者一和消费者二种分别声明了自己的队列,都绑定到了同一个在生成者端声明的交换机中。
生产者发送一条消息到交换机,绑定到了交换机的队列都可以获得这条消息,实现一条消息被多个消费者获得的功能。
在RabbitMQ的管理工具中可以查看绑定到交换机的队列,如下:
路由模式
路由模式中的元素和订阅者模式中的元素一样,但是路由模式是在订阅者模式基础上的升级。订阅者模式中的队列绑定了交换机后会接受生成者发送的所有消息,但是消费者可能并不想接受所有的消息,路由模式可以实现消息的订制。
路由模式在生产者发送消息时会声明一个key,消费者的队列在绑定交换机是会表明自己接受什么key值的消息,下面来看一下代码:
生产者代码:
public class Send {
private final static String EXCHANGE_NAME = "test_exchange_direct";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 消息内容
String message1 = "删除信息, id = 1001";
String message2 = "更新信息, id = 1002";
// 发送消息,声明消息的key
channel.basicPublish(EXCHANGE_NAME, "delete", null, message1.getBytes());
channel.basicPublish(EXCHANGE_NAME, "update", null, message2.getBytes());
System.out.println(" [x] Sent '" + message1 + "'");
System.out.println(" [x] Sent '" + message2 + "'");
channel.close();
connection.close();
}
}
消费者代码:
public class Recv {
private final static String QUEUE_NAME = "test_queue_direct_1";
private final static String EXCHANGE_NAME = "test_exchange_direct";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 绑定队列到交换机,声明接受消息的key
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "delete");
// 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 前台系统: '" + message + "'");
Thread.sleep(10);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
生产者发送了两条消息,一条key为delete,另一条key为update。在消费者端,队列绑定交互机时声明了delete这个key,这样当前消费者只会接受到delete消息,不会接收到奥update消息。
通配符模式
通配符模式中的元素和订阅者模式及路由模式中的元素一样,而通配符模式是在路由模式基础上的升级。路由模式需要消费者端的队列在绑定交换机时声明完整的key,如果需要接受多个key的消息则需要绑定多条。通配符模式就类似模糊匹配,只需要声明部分关键字即可。
#:可以匹配一个或多个词
*:只能匹配一个词
例:
item.category.add,使用#写为item.#即可匹配到,使用*则需写为item.*.*
生产者代码:
public class Send {
private final static String EXCHANGE_NAME = "test_exchange_topic";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
// 消息内容
String message1 = "删除,id = 1001";
String message2 = "更新,id = 1002";
channel.basicPublish(EXCHANGE_NAME, "item.delete", null, message1.getBytes());
channel.basicPublish(EXCHANGE_NAME, "item.update", null, message2.getBytes());
System.out.println(" [x] Sent '" + message1 + "'");
System.out.println(" [x] Sent '" + message2 + "'");
channel.close();
connection.close();
}
}
消费者代码:
public class Recv2 {
private final static String QUEUE_NAME = "test_queue_topic_2";
private final static String EXCHANGE_NAME = "test_exchange_topic";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.#");
// 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 搜索系统: '" + message + "'");
Thread.sleep(10);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
消费者端的的队列在绑定交换机时只需要写item.#即可匹配到item.delete和item.update。
小结
本篇文章中的三种消息队列包含的元素相同,功能是越来越灵活的,之所以能实现这种效果,是因为交换机有不同的模式,三种模式的生产者端在声明交换机时分别使用了不同的模式。
订阅模式——Fanout
路由模式——Direct通配模式——Topic