再看rabbitmq的交换器和队列的关系
最近又要用到rabbitmq,业务上要求服务器只发一次消息,需要多个客户端都去单独消费。但我们知道rabbitmq的机制里,每个队列里的消息只能消费一次,所以客户端要单独消费信息,就必须得每个客户端单独监听一个queue。所以我最终想实现的是服务端只声明exchange,客户端来创建queue和绑定exchange。但是在看各种rabbitmq博文和讨论的时候,我觉得对exchange的模式和queue间的关系讲的都不是很清楚。所以我决定自己验证一下
fanout模式和direct模式
本文主要验证fanout模式和direct模式下以上猜想是否可行。fanout模式就是大名鼎鼎的广播模式了,只要queue绑定了fanout的交换器,就可以直接的收到消息,无需routingkey的参与。而direct模式就是通过routing key直接发送到绑定了同样routing key的队列中。那么,在这两种exchange的模式下,是否都可以实现服务端仅创建exchange,客户端创建queue并绑定exchange呢?
Direct模式验证
我们先把交换器、routingkey、队列的名称定义好:
- 交换器为directTest
 - routingkey为direct_routing_key
 - 队列测试3个,首先测试Direct_test_queue_1,再行测试Direct_test_queue_2,再行测试Direct_test_queue_3
 
代码使用spring boot框架快速搭建。我们先规划好需要几个类来完成这个事情:
- 针对生产者,需要RabbitmqConfig,用来配置exchange的
 - 针对生产者,需要DirectRabbitSender,用来实现Direct模式的消息发送
 - 针对消费者,需要DirectConsumerOne,来测试第一个队列Direct_test_queue_1生成和消息接收
 - 针对消费者,需要DirectConsumerTwo,来测试第二个队列Direct_test_queue_2生成和消息接收
 - 针对消费者,需要DirectConsumerThree,来测试第三个队列Direct_test_queue_3生成和消息接收
 - 我们还需要一个测试类RabbitmqApplicationTests,用于测试消息的发送和接收
 
rabbitmq先配置一个DirectExchange
@Bean
DirectExchange directExchange(){
    return new DirectExchange("directTest", true, false);
}
我们可以看到Direct交换器的名称定义为了directTest,这时候还未绑定任何的队列。启动程序,若我们的设想没错,则rabbitmq中应该已经生成了directTest的exchange。

Bingo!directTest交换器成功创建。接下来,我们去编写DirectRabbitSender的代码
@Component
public class DirectRabbitSender{
    @Autowired
    private RabbitTemplate rabbitTemplate;
    private final String EXCHANGE_NAME = "directTest";
    private final String ROUTING_KEY = "direct_routing_key";
    public void send(Object message) {
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING_KEY, message);
    }
}
我们可以看到代码中,通过rabbitTemplate发送消息到了交换器为directTest,routingkey为direct_routing_key的地方。但这时候我们没有任何队列了,自然接不到消息。现在我们去编写第一个消费者DirectConsumerOne来接受消息。
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "Direct_test_queue_1", durable = "true"),
        exchange = @Exchange(value = "directTest"),
        key = "direct_routing_key"
))
public class DirectConsumerOne {
    @RabbitHandler
    private void onMessage(String message){
        System.out.println("监听队列Direct_test_queue_1接到消息" + message);
    }
}
通过代码可以看到,我们通过@QueueBinding把Direct_test_queue_1队列绑定到了directTest和direct_routing_key上。Direct_test_queue_1并没有在rabbitmq创建,这并没有关系。一般来说,@RabbitListener会自动去创建队列。启动程序,我们去看一下rabbitmq里队列是不是创建了。

Bingo!再次验证成功。我们去看看绑定关系是不是正确。这时候Direct_test_queue_1应该绑定到了名为directTest的交换器,而绑定的routingkey为direct_routing_key

biubiubiu!绑定关系完全正确。到了这里,我们进行最后一步,写了单元测试去发送消息,查看控制台中消费者是否成功收到消息。RabbitmqApplicationTests的代码如下:
@SpringBootTest
class RabbitmqApplicationTests {
    @Autowired
    private DirectRabbitSender directRabbitSender;
    @Test
    void contextLoads() {
    }
    @Test
    public void directSendTest(){
        directRabbitSender.send("direct-sender");
        directRabbitSender.send("direct-sender_test");
    }
}
启动测试类,然后去查看控制台。

没错,这就是我们想要达到的效果!基本可以宣布Direct模式验证成功。服务端生成exchange,客户端去生成队列绑定的方式在direct模式下完全可行。为了保险起见,再验证一下生成多个消费者绑定到同一个队列是否可行。
DirectConsumerTwo代码如下:
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "Direct_test_queue_2", durable = "true"),
        exchange = @Exchange(value = "directTest"),
        key = "direct_routing_key"
))
public class DirectConsumerTwo {
    @RabbitHandler
    private void onMessage(String message){
        System.out.println("监听队列Direct_test_queue_2接到消息" + message);
    }
}
DirectConsumerThree代码如下:
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "Direct_test_queue_3", durable = "true"),
        exchange = @Exchange(value = "directTest"),
        key = "direct_routing_key"
))
public class DirectConsumerThree {
    @RabbitHandler
    private void onMessage(String message){
        System.out.println("监听队列Direct_test_queue_3接到消息" + message);
    }
}
启动测试类,我们去看两个地方:
- rabbitmq是否创建了客户端绑定的三个队列Direct_test_queue_1、Direct_test_queue_2、Direct_test_queue_3
 - 消费者应该各自收到2条消息(Test中发送了两条,参看上面 RabbitmqApplicationTests 的代码)。那3个队列,控制台中应该打印了6条消息。
 

hohohoho!创建成功,并且绑定关系我看了也全都正确。我们去看控制台

6条!没有任何毛病,至此,可以宣布Direct模式下,完全支持我们最初的想法:服务端生成exchange,客户端去生成队列绑定的方式在direct模式下完全可行。
fanout模式验证
接下来我们验证一下fanout的方式,基本操作流程和Direct模式一致。代码的结构也差不多:
- 针对生产者,需要RabbitmqConfig,直接在Direct模式下的rabbitmqConfig里直接添加Fanout的交换器配置
 - 针对生产者,需要FanoutRabbitSender,用来实现Fanout模式的消息发送
 - 针对消费者,需要FanoutConsumerOne,来测试第一个队列Fanout_test_queue_1生成和消息接收
 - 针对消费者,需要FanoutConsumerTwo,来测试第二个队列Fanout_test_queue_2生成和消息接收
 - 针对消费者,需要FanoutConsumerThree,来测试第三个队列Fanout_test_queue_3生成和消息接收
 - 测试类RabbitmqApplicationTests也直接复用Direact模式下测试的类
 
我就不多BB,直接上代码了。
RabbitmqConfig代码如下
@Configuration
public class RabbitmqConfig {
    @Bean
    DirectExchange directExchange(){
        return new DirectExchange("directTest", true, false);
    }
    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanoutTest", true, false);
    }
}
FanoutRabbitSender的代码如下,此处和direct模式的区别是Fanout中没有routingkey,所以代码里也没定义routingkey:
@Component
public class FanoutRabbitSender{
    @Autowired
    private RabbitTemplate rabbitTemplate;
    private final String EXCHANGE_NAME = "fanoutTest";
    public void send(Object message) {
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, null, message);
    }
}
我们到这里先启动程序试试,看看fanoutTest的交换器在没有绑定队列的情况下是否生成了。

棒棒棒!和我们想的一样,那接下来去写完所有的消费者,这里和Direct模式最重要的区别是@Exchange中必须要指定type为fanout。direct模式的代码里没指定是因为@Exchange的type默认值就是direct。我直接上代码了:
/**
 * 监听器主动去声明queue=fanout_test_queue_1,并绑定到fanoutTest交换器
 */
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "fanout_test_queue_1", durable = "true"),
        exchange = @Exchange(value = "fanoutTest", type = ExchangeTypes.FANOUT)
))
public class FanoutConsumerOne {
    @RabbitHandler
    private void onMessage(String message){
        System.out.println("监听队列fanout_test_queue_1接到消息" + message);
    }
}
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "fanout_test_queue_2", durable = "true"),
        exchange = @Exchange(value = "fanoutTest", type = ExchangeTypes.FANOUT)
))
public class FanoutConsumerTwo {
    @RabbitHandler
    private void onMessage(String message){
        System.out.println("监听队列fanout_test_queue_2接到消息" + message);
    }
}
@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "fanout_test_queue_3", durable = "true"),
        exchange = @Exchange(value = "fanoutTest", type = ExchangeTypes.FANOUT)
))
public class FanoutConsumerThree {
    @RabbitHandler
    private void onMessage(String message){
        System.out.println("监听队列fanout_test_queue_3接到消息" + message);
    }
}
接着去测试类RabbitmqApplicationTests中加上fanout的发送测试,然后注释掉direct的单元测试,以便一会造成干扰
@SpringBootTest
class RabbitmqApplicationTests {
    @Autowired
    private DirectRabbitSender directRabbitSender;
    @Autowired
    private FanoutRabbitSender fanoutRabbitSender;
    @Test
    void contextLoads() {
    }
//    @Test
//    public void directSendTest(){
//        directRabbitSender.send("direct-sender");
//        directRabbitSender.send("direct-sender_test");
//    }
    @Test
    public void fanoutSendTest(){
        fanoutRabbitSender.send("fanout-sender_1");
        fanoutRabbitSender.send("fanout-sender_2");
    }
}
代码都完成了,现在我们启动测试类,看看控制台是否正常收到了消息

看图看图,fanout模式下也完全认证成功!!!那我们可以宣布,文章开头的猜想完全可以实现。
总结
服务端只声明exchange,客户端来创建queue和绑定exchange的方式完全可行。并且在Direct和Fanout模式下都可行。
那我们可以推测在Header模式的交换器和Topic模式的交换器下应该也大差不差。具体各位可自行验证,基本流程和上面direct和fanout的流程差不多。
再看rabbitmq的交换器和队列的关系的更多相关文章
- RabbitMQ中声明交换器,队列时的,autoDelete=true自动删除的条件
		
在声明交换器和队列时,有一个属性叫autoDelete,表示是否自动删除. 如果autoDelete=true,表示自动删除.此处我们要理解,自动删除的条件是什么? 这里的关键是,自动删除的条件是向后 ...
 - RabbitMQ详解(三)------RabbitMQ的五种队列
		
上一篇博客我们介绍了RabbitMQ消息通信中的一些基本概念,这篇博客我们介绍 RabbitMQ 的五种工作模式,这也是实际使用RabbitMQ需要重点关注的. 这里是RabbitMQ 官网中的相关介 ...
 - AMQP协议与RabbitMQ、MQ消息队列的应用场景
		
什么是AMQP? 在异步通讯中,消息不会立刻到达接收方,而是被存放到一个容器中,当满足一定的条件之后,消息会被容器发送给接收方,这个容器即消息队列,而完成这个功能需要双方和容器以及其中的各个组件遵守统 ...
 - 三.RabbitMQ之异步消息队列(Work Queue)
		
上一篇文章简要介绍了RabbitMQ的基本知识点,并且写了一个简单的发送和接收消息的demo.这一篇文章继续介绍关于Work Queue(工作队列)方面的知识点,用于实现多个工作进程的分发式任务. 一 ...
 - spring boot Rabbitmq集成,延时消息队列实现
		
本篇主要记录Spring boot 集成Rabbitmq,分为两部分, 第一部分为创建普通消息队列, 第二部分为延时消息队列实现: spring boot提供对mq消息队列支持amqp相关包,引入即可 ...
 - 再看Scrapy(1) 基本概念
		
再看Scrapy(1) 基本概念 1 准备 安装scrapy: 国内镜像源(官方的pypi不稳定)安装 pip3 install -i https://pypi.douban.com/simple/ ...
 - 再看GS接包过程
		
再看GS接包过程 bool GameServer::ProcessLoop(packet& rPkt) { if(false == m_spDataLayer->Recv(rPkt)) ...
 - 再看GS线程
		
再看GS线程 void GameServer::ProcessThreadTry() { ; packet rcvPkt; rcvPkt.data = * ]; //该事件工厂主要创建了两个定时器1. ...
 - RabbitMQ五:生产者--队列--多消费者
		
一.生成者-队列-多消费者(前言) 上篇文章,我们做了一个简单的Demo,一个生产者对应一个消费者,本篇文章就介绍 生产者-队列-多个消费者,下面简单示意图 P 生产者 C 消费者 中间队列 ...
 
随机推荐
- Spring_配置Bean & 属性配置细节
			
1.Spring容器 在 Spring IOC 容器读取 Bean 配置创建 Bean 实例之前, 必须对它进行实例化. 只有在容器实例化后, 才可以从 IOC 容器里获取 Bean 实例并使用.Sp ...
 - jchdl - RTL实例 - Mux
			
https://mp.weixin.qq.com/s/OmQRQU2mU2I5d-qtV4PAwg 二选一输出. 参考链接 https://github.com/wjcdx/jchdl/blo ...
 - 带你学够浪:Go语言基础系列-环境配置和 Hello world
			
文章每周持续更新,原创不易,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 前面几周陆陆续续写了一些后端技术的文章,包括数据库.微 ...
 - Java实现 LeetCode 777 在LR字符串中交换相邻字符(分析题)
			
777. 在LR字符串中交换相邻字符 在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作.一次移动操作指用一个"L ...
 - Java实现蓝桥杯 算法提高 八皇后 改
			
**算法提高 8皇后·改** 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 规则同8皇后问题,但是棋盘上每格都有一个数字,要求八皇后所在格子数字之和最大. 输入格式 一个8*8的棋 ...
 - Java实现 LeetCode 357 计算各个位数不同的数字个数
			
357. 计算各个位数不同的数字个数 给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10n . 示例: 输入: 2 输出: 91 解释: 答案应为除去 11, ...
 - Java实现第八届蓝桥杯图形排版
			
标题:图形排版 小明需要在一篇文档中加入 N 张图片,其中第 i 张图片的宽度是 Wi,高度是 Hi. 假设纸张的宽度是 M,小明使用的文档编辑工具会用以下方式对图片进行自动排版: 1. 该工具会按照 ...
 - Java实现指定年份月份的日历表
			
输入指定的年份与月份,看这个月的日历表 package Xueying_Liu; import java.util.Scanner; public class rilibiao { public st ...
 - Java实现 蓝桥杯 历届试题 地宫取宝
			
问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被带到地宫的入口,国王要求他只能向右或向下行走. 走 ...
 - Java实现 黑洞数
			
任意一个5位数,比如:34256,把它的各位数字打乱,重新排列,可以得到一个最大的数:65432,一个最小的数23456.求这两个数字的差,得:41976,把这个数字再次重复上述过程(如果不足5位,则 ...