文章目录
第三种模型(Publish/Subscribe 发布/订阅)
扇型(funout)交换机
扇型交换机将消息路由给绑定到它身上的所有队列,而不会理会绑定的路由键。如果 N 个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的 N 个队列。扇型用来交换机处理消息的广播路由。
因为扇型交换机投递信息的拷贝到所有绑定到它的队列,所以它可以用来在群聊的时候,分发消息给参与群聊的用户。
扇型交换机图例:
Public/Subscribe 模型
在这种模式下,消息发送流程是这样的:
- 可以有多个消费者;
- 每个消费者有自己的 queue(队列)
- 每个队列都要绑定到 Exchange(交换机)
- 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
- 交换机把所有的消息发送给绑定过的所有队列。
- 队列的消费者都能拿到消息。实现一条消息被多个消费者消费。
- 开发生产者
// 将通道声明指定的交换机
// 参数1:交换机的名称
// 参数2:交换机的类型
channel.exchangeDeclare("logs","fanout");
// 发送消息
channel.basicPublish("logs","",null,"fanout type message".getBytes());
- 开发消费者
// 通道声明交换机
channel.exchangeDeclare("logs","fanout");
// 临时队列
String queueName = channel.queueDeclare().getQueue();
// 队列绑定交换机
channel.queueBind(queueName,"logs","");
实例代码:
Runnable myRunnable = () -> {
try {
Connection conn = RabbitMQUtils.getConnection();
Channel channel = conn.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("消费者" + consumerTag + ":" + new String(body));
}
});
} catch (IOException e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
Thread t3 = new Thread(myRunnable);
t1.start();
t2.start();
t3.start();
结果(有群发那味了):
绑定
- 绑定是交换机将消息路由给队列所需遵循的规则。如果要制定交换机“E”将消息路由给队列“Q”,那么“Q”就需要与“E”进行绑定。绑定操作需要定义一个可选的路由键(routing key)属性给某些类型的交换机(当然像这第三种模型这样的,路由键有没有无所谓的,则不需声明路由键,而前两种模型下,是自动绑定的,交换机则为默认交换机,路由键和队列名一致的。)。路由键的意义在于从发送给交换机的众多消息中选择出某些消息,将其路由给绑定的队列,然后消费者再从队列中消费消息。
第四、第五种模型(Routing、Topics)
第四种模型(Routing)
在第三种模型中,一条消息会被所有订阅的队列都消费。但是,在某些场景下,希望不同的消息被不同的队列消费。这时候就需要用直连交换机构建的路由routing模型了。
图解:
- P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给与routing key完全匹配的队列。
- C1:消费者,其所在队列指定了需要routing key 为error的消息。
- C2:消费者,其所在队列指定了需要routing key 为 info、error、warning的消息。
- 开发生产者
// 通过通道声明交换机
channel.exchangeDeclare("logs_routing", "direct");
// 发送消息
String routingKey = "info";
channel.basicPublish("logs_routing", routingKey, null, ("这是direct模型发布的基于route key:" + routingKey).getBytes());
- 开发消费者
// 通道声明交换机以及交换的类型
channel.exchangeDeclare("logs_routing", "direct");
// 创建一个临时队列
String queueName = channel.queueDeclare().getQueue();
// 基于route key 绑定队列和交换机
channel.queueBind(queueName, "logs_routing", "error");
主题交换机(Topic Exchange)
主题交换机通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列(注意这是消息给一个或多个队列,和直连交换机可不同,直连是路由键和队列同名,一个消息对应一个队列这种才是直连)。主题交换机经常用来实现各种分发/订阅模式及其变种。主题交换机通常用来实现消息的多播路由(与第三种模型的广播路由不同)。
主题交换机拥有非常广泛的用户案例。当一个问题涉及到那些想要有针对性的选择需要接收消息的多消费/多应用
的时候,主题交换机都可以被列入考虑范围。
第五种模型(Topics)
主题交换机和直连交换机相比,都是可以根据 routingkey 把消息路由到不同的队列。只不过主题交换机可以让队列在绑定 routingkey 的时候使用通配符。这种模型 routingkey 一般都是由一个或者多个单词组成,多个单词之间以.
分割,例如:item.insert
通配符
* (star) can substitute for exactly one word. 匹配一个单词
# (hash) can substitute for zero or more words. 匹配一个或多个单词
如:
audit.# 匹配audit.irs.corporate 或者 audit.irs 等
audit.* 只能匹配audit.irs
- 生产者
// 声明交换机以及交换机类型
channel.exchangeDeclare("topics", "topic");
// 发布消息
String routeKey = "user.delete";
channel.basicPublish("topics", routeKey, null, ("这里是topic动态路由模型,route key:" + routeKey).getBytes());
- 消费者
// 生命交换机以及交换机类型
channel.exchangeDeclare("topics", "topic");
// 创建一个临时队列
String queueName = channel.queueDeclare().getQueue();
// 绑定队列和交换机 动态通配符形式 route key
channel.queueBind(queueName, "topics", "use.*");
案例结果