消息队列模型之fanout 模型
fanout 扇出 也称为广播

在广播模式下,消息发送流程是这样的:
-
可以有多个消费者
-
每个消费者有自己的queue(队列)
-
每个队列都要绑定到Exchange(交换机)
-
生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
-
交换机把消息发送给绑定过的所有队列
-
队列的消费者都能拿到消息。实现一条消息被多个消费者消费
具体的实现步骤
1. 开发生产者
package com.ztb.fanoutqueues;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Provider {
public static void main(String[] args) throws IOException {
//获取连接对象
Connection connection = RabbitmqUtils.getConnection();
Channel channel = connection.createChannel();
//将通道声明指定交换机 参数1:交换机名称 参数2:交换机类型 fanout 播类型
channel.exchangeDeclare("logs","fanout");
//发送消息
channel.basicPublish("logs","",null,"fanout type message".getBytes());
RabbitmqUtils.closeConnectionAndChanel(channel,connection);
}
}
2. 开发消费者-1
package com.ztb.fanoutqueues;
import com.rabbitmq.client.*;
import com.rabbitmq.client.impl.ChannelN;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer {
public static void main(String[] args) throws IOException {
//获取来连接对象
Connection connection= RabbitmqUtils.getConnection();
//获取通道
Channel channel=connection.createChannel();
//通道绑定交换机
channel.exchangeDeclare("logs","fanout");
//创建一个临时队列
String queueName = channel.queueDeclare().getQueue();
//绑定交换机和队列
channel.queueBind(queueName,"logs","");
//消费队列
channel.basicConsume(queueName,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者: "+new String(body));
}
});
}
}
3. 开发消费者-2
package com.ztb.fanoutqueues;
import com.rabbitmq.client.*;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取来连接对象
Connection connection= RabbitmqUtils.getConnection();
//获取通道
Channel channel=connection.createChannel();
//通道绑定交换机
channel.exchangeDeclare("logs","fanout");
//创建一个临时队列
String queueName = channel.queueDeclare().getQueue();
//绑定交换机和队列
channel.queueBind(queueName,"logs","");
//消费队列
channel.basicConsume(queueName,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));
}
});
}
}
4.开发消费者-3
package com.ztb.fanoutqueues;
import com.rabbitmq.client.*;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
//获取来连接对象
Connection connection= RabbitmqUtils.getConnection();
//获取通道
Channel channel=connection.createChannel();
//通道绑定交换机
channel.exchangeDeclare("logs","fanout");
//创建一个临时队列
String queueName = channel.queueDeclare().getQueue();
//绑定交换机和队列
channel.queueBind(queueName,"logs","");
//消费队列
channel.basicConsume(queueName,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));
}
});
}
}
测试结果:

交换机中产生了名为log的交换机,以及绑定了三个消息队列。

消息队列模型之Routing
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. 开发生产者
package com.ztb.directqueue;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Provider {
public static void main(String[] args) throws IOException {
//获得连接对象
Connection connection= RabbitmqUtils.getConnection();
//获得连接通道
Channel channel=connection.createChannel();
//通道绑定交换机
channel.exchangeDeclare("logs direct","direct");
//参数说明:参数1:交换机名称 参数2:type: direct :路由模式
//发布消息
String routingkey="info";//指定后,该消息是发给info队列的
channel.basicPublish("logs direct","info",null,("这是direct模型发布的基于route key:("+routingkey+")").getBytes());
RabbitmqUtils.closeConnectionAndChanel(channel,connection);
}
}
2.开发消费者1
package com.ztb.directqueue;
import com.rabbitmq.client.*;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取来连接对象
Connection connection= RabbitmqUtils.getConnection();
//获取通道
Channel channel=connection.createChannel();
//通道绑定交换机
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));
}
});
}
}
3.开发消费者2
package com.ztb.directqueue;
import com.rabbitmq.client.*;
import com.ztb.utils.RabbitmqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
//获取来连接对象
Connection connection= RabbitmqUtils.getConnection();
//获取通道
Channel channel=connection.createChannel();
//通道绑定交换机
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));
}
});
}
}
4.测试生产者发送Route key为error的消息时、

5.测试生产者发送Route key为info的消息时


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.开发消费者
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));
}
});
4.测试结果

本文深入探讨了消息队列的三种主要模型:Fanout、Direct和Topic。Fanout模型通过广播方式使每条消息被所有消费者接收;Direct模型允许根据RoutingKey将消息精确路由到指定队列;Topic模型则引入了通配符机制,使得队列能够根据模式匹配接收消息。
3683

被折叠的 条评论
为什么被折叠?



