开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
4.6 第四种模型(Routing)
4.6.1 Routing 之订阅模型-Direct(直连)
在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
在Direct模型下:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey(路由key) - 消息的发送方在 向 Exchange发送消息时,也必须指定消息的
RoutingKey。 - Exchange不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key进行判断,只有队列的Routingkey与消息的Routing key完全一致,才会接收到消息
流程:
图解:
- P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
- C1:消费者,其所在队列指定了需要routing key 为 error 的消息
- C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息
1. 开发生产者
//声明交换机 参数1:交换机名称 参数2:交换机类型 基于指令的Routing key转发
channel.exchangeDeclare("logs_direct","direct");
String key = "";
//发布消息
channel.basicPublish("logs_direct",key,null,("指定的route key"+key+"的消息").getBytes());
生产者类:
public class Provider {
public static void main(String[] args) throws IOException {
// 获取连接对象
Connection connection = RabbitMQUtils.getConnection();
// 获取通道对象
Channel channel = connection.createChannel();
// 定义交换机名字
String exchangeName = "logs_direct";
// 通过通道声明交换机 交换机名字 交换机类型:direct路由模式
channel.exchangeDeclare(exchangeName, "direct");
// 定义routingKey (内容写什么都行,相当于一个标识)
String routingKey = "info";
// 发送消息 交换机名字 路由key 消息是否持久化
channel.basicPublish(exchangeName, routingKey, null, ("这是direct模型发布的基于route key: [" + routingKey + "] 发送的消息").getBytes());
// 关闭资源
RabbitMQUtils.closeConnectionAndChanel(channel, connection);
}
}
2.开发消费者-1
//声明交换机
channel.exchangeDeclare("logs_direct","direct");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定队列和交换机
channel.queueBind(queue,"logs_direct","error");
channel.queueBind(queue,"logs_direct","info");
channel.queueBind(queue,"logs_direct","warn");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1: "+new String(body));
}
});
消费者1类:
public class Consumer1 {
public static void main(String[] args) throws IOException {
// 创建连接对象
Connection connection = RabbitMQUtils.getConnection();
// 创建通道对象
Channel channel = connection.createChannel();
// 定义交换机名字
String exchangeName = "logs_direct";
// 通道声明(绑定)交换机以及交换机的类型 交换机的名字 交换机的类型
channel.exchangeDeclare(exchangeName, "direct");
// 创建一个临时队列名字
String queue = channel.queueDeclare().getQueue();
// 基于routeKey绑定队列和交换机 队列的名字 交换机名字 路由key
channel.queueBind(queue, exchangeName, "error");
// 消费消息 队列名字 自动确认 回调接口:用来消费消息
channel.basicConsume(queue, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:" + new String(body));
}
});
}
}
3.开发消费者-2
//声明交换机
channel.exchangeDeclare("logs_direct","direct");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定队列和交换机
channel.queueBind(queue,"logs_direct","error");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2: "+new String(body));
}
});
消费者2类:
public class Consumer2 {
public static void main(String[] args) throws IOException {
// 创建连接对象
Connection connection = RabbitMQUtils.getConnection();
// 创建通道对象
Channel channel = connection.createChannel();
// 定义交换机名字
String exchangeName = "logs_direct";
// 通道声明(绑定)交换机以及交换机的类型 交换机的名字 交换机的类型
channel.exchangeDeclare(exchangeName, "direct");
// 创建一个临时队列名字
String queue = channel.queueDeclare().getQueue();
// 基于routeKey绑定队列和交换机 队列的名字 交换机名字 路由key
channel.queueBind(queue, exchangeName, "info");
channel.queueBind(queue, exchangeName, "error");
channel.queueBind(queue, exchangeName, "warning");
// 消费消息 队列名字 自动确认 回调接口:用来消费消息
channel.basicConsume(queue, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:" + new String(body));
}
});
}
}
4.测试生产者发送Route key为error的消息时
5.测试生产者发送Route key为info的消息时
4.6.2 Routing 之订阅模型-Topic(第五种模型)
Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!这种模型Routingkey 一般都是由一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
# 统配符
* (star) can substitute for exactly one word. 匹配不多不少恰好1个词
# (hash) can substitute for zero or more words. 匹配一个或多个词
# 如:
audit.# 匹配audit.irs.corporate或者 audit.irs 等
audit.* 只能匹配 audit.irs
1.开发生产者
//生命交换机和交换机类型 topic 使用动态路由(通配符方式)
channel.exchangeDeclare("topics","topic");
String routekey = "user.save";//动态路由key
//发布消息
channel.basicPublish("topics",routekey,null,("这是路由中的动态订阅模型,route key: ["+routekey+"]").getBytes());
生产者类:
2.开发消费者-1
Routing Key中使用*通配符方式
//声明交换机
channel.exchangeDeclare("topics","topic");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定队列与交换机并设置获取交换机中动态路由
channel.queueBind(queue,"topics","user.*");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1: "+new String(body));
}
});
3.开发消费者-2
Routing Key中使用#通配符方式
//声明交换机
channel.exchangeDeclare("topics","topic");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定队列与交换机并设置获取交换机中动态路由
channel.queueBind(queue,"topics","user.#");
//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2: "+new String(body));
}
});