中间件系列六 RabbitMQ之Topic exchange 用法

588 阅读4分钟
原文链接: blog.csdn.net

1. 概述

上篇文章中间件系列五 RabbitMQ之Direct exchange(直连交换机)和路由,我们通过direct exchange(直连交换机)可以根据路由键进行路由,但是还是不够灵活,它只能进行完全匹配。这节我们引入Topic exchange(主题交换机),支持对路由键的模糊匹配
上篇文章实现生产者发送一个消息,这个消息同时被传送给所有队列。但是有时我们不希望所有的消息都被所有队列接收,我们希望可以指定类型为a的消息只能被队列A接收,类型为b的消息只能被队列B,C接收。扇型交换机只能无脑地广播消息给所有的消费者,实质是广播给所有关联的队列。
为了实现这个功能,一种是建立多个交换机,这种方式简单暴力但是不灵活。本节我们介绍使用单个直连交换机+路由实现以上功能

2. 本文实现功能说明

这里写图片描述

上图中有3个绑定

队列Q1绑定键值"*.orange.*"
队列Q2绑定键值"*.*.rabbit", "lazy.#"

案例说明

1. 路由键"quick.orange.rabbit"和"lazy.orange.elephant"会被同时投递到Q1和Q2
2. 路由键"quick.orange.fox" 只被同时投递到Q1
3. 路由键"lazy.brown.fox" 只被投递到Q2
4. 路由键"lazy.pink.rabbit" 只会被投递到Q2,且只投递一次,尽管它同时符合两个键的值"*.*.rabbit", lazy.#"。
5. 路由键"quick.brown.fox"、"orange" 、 "quick.orange.male.rabbit" 没有符合两个队列的要求,被丢弃

路由键中特殊匹配字符说明

  • *(星号)可以代替一个字。
  • #(散列)可以代替零个或多个单词。

Topic Exchange的路由键条件:

  1. 必须是由英文单词列表组成,单词之间使用”.”分隔
  2. 路由键的长度最大255字节

3. 生产者代码

主要业务逻辑如下:

1. 配置连接工厂
2. 建立TCP连接
3. 在TCP连接的基础上创建通道
4. 声明一个topic交换机 
5. 发送消息,并配置消息的路由键

此代码和上一篇的代码基本相同,最大的不同是声明了topic交换机

// 声明一个topic交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

完整代码
发送者代码: TopicsSend.java

4. 接收者代码

主要业务逻辑如下:

1. 配置连接工厂
2. 建立TCP连接
3. 在TCP连接的基础上创建通道
4. 声明一个topic交换机 
5. 声明一个临时队列
6. 将临时队列绑定到交换机上,并在队列上绑定多个绑定值
7. 接收消息并处理

此代码和上一篇的代码基本相同,最大的不同是声明了topic交换机

// 声明一个topic交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

完整代码
消费者代码:TopicsRecv.java

5. 测试

BasicTest

@Test
public void topics() throws InterruptedException {

   // 消费者1:绑定 *.orange.* 值
   executorService.submit(() -> {
       String[] bindingKeys = {"*.orange.*"};
       TopicsRecv.execute(rabbitmq_host, rabbitmq_user, rabbitmq_pwd, bindingKeys);
   });

   // 消费者2:绑定  "*.*.rabbit" 和 "lazy.#"值
   executorService.submit(() -> {
       String[] bindingKeys = {"*.*.rabbit", "lazy.#"};
       TopicsRecv.execute(rabbitmq_host, rabbitmq_user, rabbitmq_pwd, bindingKeys);
   });

   Thread.sleep(5* 100);
   // 生产者1 : 发送 black,所有的接收端都会收到
   executorService.submit(() -> {
       String routing = "quick.orange.rabbit";
       TopicsSend.execute(rabbitmq_host, rabbitmq_user, rabbitmq_pwd, routing);
   });

   // 生产者2 : 发送 green,所有的接收端都不会收到
   executorService.submit(() -> {
       String routing = "lazy.pink.rabbit";
       TopicsSend.execute(rabbitmq_host, rabbitmq_user, rabbitmq_pwd, routing);
   });

   // sleep 10s
   Thread.sleep(10 * 1000);
}

以上代码启动2个消费者,消费者1绑定”*.orange.*” ,消费者2个绑定”*.*.rabbit” 和 “lazy.#”;
启动2个生产者,生产者1发送消息的路由键为 “quick.orange.rabbit”,此消息同时被2个消费者接收
生产者2发送消息的路由键为”lazy.pink.rabbit”,此消息只被消费者2接收,符合之前的分析

// 启动2个消费者,消费者1绑定"*.orange.*" ,消费者2个绑定"*.*.rabbit" 和 "lazy.#"
[TopicsRecv [*.orange.*]] Waiting for messages.
[TopicsRecv [*.*.rabbit, lazy.#]] Waiting for messages.
// 生产者1发送消息的路由键为 "quick.orange.rabbit",此消息同时被2个消费者接收
[TopicsSend] Sent 'quick.orange.rabbit':'Topics-1516008766983'
[TopicsRecv [*.*.rabbit, lazy.#] ] Received 'quick.orange.rabbit':'Topics-1516008766983'
[TopicsRecv [*.orange.*] ] Received 'quick.orange.rabbit':'Topics-1516008766983'
// 生产者2发送消息的路由键为"lazy.pink.rabbit",此消息只被消费者2接收
[TopicsSend] Sent 'lazy.pink.rabbit':'Topics-1516008767012'
[TopicsRecv [*.*.rabbit, lazy.#] ] Received 'lazy.pink.rabbit':'Topics-1516008767012'

6. 代码

上文的详细代码主要如下:
发送者代码: TopicsSend.java
消费者代码: TopicsRecv.java
测试代码:BasicTest.java的方法 topics()
所有的详细代码见github代码,请尽量使用tag v0.10,不要使用master,因为master一直在变,不能保证文章中代码和github上的代码一直相同