本文代码基于SpringBoot,文末有代码连接 。首先是一些在Spring Boot的一些配置和概念,然后跟随代码看下五种模式

MQ两种消息传输方式,点对点(代码中的简单传递模式),发布/订阅(代码中路由模式)。要是你熟悉RabbitMQ SpringBoot配置的话,就是simple和direct。

MQ安装指南:https://blog.csdn.net/qq_19006223/article/details/89421050

0.消息队列运转过程

生产者生产过程:
(1)生产者连接到 RabbitMQ Broker 建立一个连接( Connection) ,开启 个信道 (Channel)
(2) 生产者声明一个交换器 ,并设置相关属性,比如交换机类型、是否持久化等
(3)生产者声明 个队列井设置相关属性,比如是否排他、是否持久化、是否自动删除等
(4)生产者通过路由键将交换器和队列绑定起来。
(5)生产者发送消息至 RabbitMQ Broker ,其中包含路由键、交换器等信息。
(6) 相应的交换器根据接收到的路由键查找相匹配的队列 如果找到 ,则将从生产者发送过来的消息存入相应的队列中。
(7) 如果没有找到 ,则根据生产者配置的属性选择丢弃还是回退给生产者
(8) 关闭信道。
(9) 关闭连接。

消费者接收消息的过程:
(1)消费者连接到 RabbitMQ Broker ,建立一个连接(Connection ,开启 个信道(Channel)
(2) 消费者向 RabbitMQ Broker 请求消费相应队列中的消息,可能会设置相应的回调函数, 以及做 些准备工作。
(3)等待 RabbitMQ Broker 回应并投递相应队列中的消息, 消费者接收消息。
(4) 消费者确认 ack) 接收到的消息
(5) RabbitMQ 从队列中删除相应己经被确认的消息
(6) 关闭信道。
(7)关闭连接。

1.项目结构

common是工具,receiver是消费者,sender是生产者

具体各自的pom.xml文件请看项目,都有注释。

2.sender(生产者的配置)

   

#确认机制
publisher-confirms: true 消息有没有到达MQ(会返回一个ack确认码)
publisher-returns: true 消息有没有找到合适的队列
主要是为了生产者和mq之间的一个确认机制,当消息到没到mq,会提供相应的回调,在项目中 RabbitSender 这个类中进行了相应的配置
     private final RabbitTemplate.ConfirmCallback confirmCallback = (correlationData, ack, s) -> {
if (ack) {
System.out.println(correlationData.getId());
} else {
log.error("ConfirmCallback消息发送失败: {}", s);
}
}; private final RabbitTemplate.ReturnCallback returnCallback = (message, replyCode, replyText, exchange, routingKey)
-> log.error("ReturnCallback消息发送失败: {}", new String(message.getBody(), StandardCharsets.UTF_8)); public <T> void sendMsg(String exchangeName, String routingKeyName, T content) {
// 设置每个消息都返回一个确认消息
this.rabbitTemplate.setMandatory(true);
// 消息确认机制
this.rabbitTemplate.setConfirmCallback(confirmCallback);
// 消息发送失败机制
this.rabbitTemplate.setReturnCallback(returnCallback);
// 异步发送消息
CorrelationData correlationData = new CorrelationData();
correlationData.setId("123");
this.rabbitTemplate.convertAndSend(exchangeName, routingKeyName, content, correlationData);
}

 还可以根据需求设置发送时CorrelationData 的值

#mandatory

参数设为 true 时,交换器无法根据自身的类型和路由键找到一个符合条件 的队列,那么 RabbitM 会调用 Basic.Return 命令将消息返回给生产者。
默认为false,直接丢弃 3.receiver(消费者配置)

这里主要说一下 listerner 的相关配置

一共有两种模式:simple和direct模式

simple主要包括两种工作模式,direct主要包括四种,待会代码会详解。

先说主要配置(以direct为例)

#acknowledge-mode: manual

手动确认模式,推荐使用这种。就是说当消息被消费者消费时,需要手动返回信息告诉mq。如果是自动的话,mq会自动确认,不管你消费者是否完成消费(比如说抛出异常)

#prefetch: 1

一个消费者一次拉取几条消息,本demo一条一条来。

#consumers-per-queue: 2

一个队列可以被多少消费者消费(这个配置,我测试的时候没测试出来,如果有朋友了解的话,可以评论下。)

还有其他配置,看下源码,两种模式共有的

simple特有的

direct特有的

4.各种模式详解

---------simple方式下的两种

打开上面的listener配置

4.1 simple

一个生产者,一个消费者

生产者发送消息都在SenderTest里面

生产者:

    /**简单模式*/
@Test
public void senderSimple() throws Exception {
String context = "simple---> " + new Date();
this.rabbitTemplate.convertAndSend("simple", context);
}

消费者

    @RabbitListener(queues = "simple")
public void simple(Message message, Channel channel){
String messageRec = new String(message.getBody());
System.out.println("simple模式接收到了消息:"+messageRec);
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
System.out.println("报错了------------------"+e.getMessage());
}
}

输出

  

simple模式接收到了消息:simple---> Sat Apr 20 20:40:16 CST 2019

4.2 work 模式

一个生产者,多个消费者

生产者

    private static final List<Integer> ints = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/**work模式*/
@Test
public void senderWork() throws Exception {
ints.forEach((i)->{
String context = "work---> " + i;
this.rabbitTemplate.convertAndSend("work", context);
});
}

消费者

    @RabbitListener(queues = "work")
public void work1(Message message, Channel channel){
try{
Thread.sleep(500);
}catch (Exception e){
System.out.println(e.getMessage());
}
String messageRec = new String(message.getBody());
System.out.println("work1接收到了消息:"+messageRec);
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
System.out.println("work1报错了------------------"+e.getMessage());
}
} @RabbitListener(queues = "work")
public void work2(Message message, Channel channel){
try{
Thread.sleep(1000);
}catch (Exception e){
System.out.println(e.getMessage());
}
String messageRec = new String(message.getBody());
System.out.println("work2接收到了消息:"+messageRec);
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
System.out.println("work2报错了------------------"+e.getMessage());
}
}

输出

work1接收到了消息:work---> 2
work2接收到了消息:work---> 0
work1接收到了消息:work---> 1
work1接收到了消息:work---> 4
work2接收到了消息:work---> 3
work1接收到了消息:work---> 6
work1接收到了消息:work---> 7
work2接收到了消息:work---> 5
work1接收到了消息:work---> 8
work1接收到了消息:work---> 10
work2接收到了消息:work---> 9

-----direct方式下的

切换listener配置

4.3direct交换机

生产者发送消息给指定交换机,绑定的某个队列。

消费者通过监听某交换机绑定的某个队列接受消息。

生产者

    /**direct交换机*/
@Test
public void senderDirect() throws Exception {
rabbitSender.sendMsg("direct","directKey1","directContent1");
rabbitSender.sendMsg("direct","directKey2","directContent2");
}

消费者

    @RabbitListener(bindings = @QueueBinding(exchange = @Exchange("direct"), key = "directKey1"
, value = @Queue(value = "directQueue1", durable = "true", exclusive = "false", autoDelete = "false")))
public void direct1(String str, Channel channel, Message message) throws IOException {
try {
System.out.println("directQueue1接收到了:"+str);
}catch (Exception e){
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
} @RabbitListener(bindings = @QueueBinding(exchange = @Exchange("direct"), key = "directKey2"
, value = @Queue(value = "directQueue2", durable = "true", exclusive = "false", autoDelete = "false")))
public void direct2(String str, Channel channel, Message message) throws IOException {
try {
System.out.println("directQueue2接收到了:"+str);
}catch (Exception e){
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
}

输出

directQueue1接收到了:directContent1
directQueue2接收到了:directContent2

4.4 topic交换机

指定主题

# :匹配一个或者多级路径

*: 匹配一级路径

生产者

    @Test
public void senderTopic() throws Exception {
String contexta = "topic.a";
rabbitSender.sendMsg("topic","topicKey.a",contexta);
String contextb = "topic.b";
rabbitSender.sendMsg("topic","topicKey.b",contextb);
String contextc = "topic.c";
rabbitSender.sendMsg("topic","topicKey.c",contextc);
String contextz = "topic.z";
rabbitSender.sendMsg("topic","topicKey.c.z",contextz);
}

消费者

    /**
* topic交换机
* */
@RabbitListener(bindings = @QueueBinding(exchange = @Exchange(name = "topic",type = "topic"), key = "topicKey.#"
, value = @Queue(value = "topicQueue", durable = "true", exclusive = "false", autoDelete = "false")))
public void topicQueue(String str, Channel channel, Message message) throws Exception {
try {
System.out.println("topicQueue接收到了:"+str);
}catch (Exception e){
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
}

输出

topicQueue接收到了:topic.a

4.5 Fanout 交换机

广播模式,一个消息可以给多个消费者消费

生产者

    /**Fanout 交换机*/
@Test
public void senderFanout() throws Exception {
String contexta = "Fanout";
rabbitSender.sendMsg("fanout","fanoutKey1",contexta);
//写不写KEY都无所谓
}

消费者

    /**
* Fanout 交换机
* */
@RabbitListener(bindings = @QueueBinding(exchange = @Exchange(name = "fanout",type = "fanout"), key = "fanoutKey1"
, value = @Queue(value = "fanoutQueue1", durable = "true", exclusive = "false", autoDelete = "false")))
public void fanoutQueue1(String str, Channel channel, Message message) throws Exception {
try {
System.out.println("fanoutQueue1接收到了:"+str);
}catch (Exception e){
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
} @RabbitListener(bindings = @QueueBinding(exchange = @Exchange(name = "fanout",type = "fanout"), key = "fanoutKey2"
, value = @Queue(value = "fanoutQueue2", durable = "true", exclusive = "false", autoDelete = "false")))
public void fanoutQueue2(String str, Channel channel, Message message) throws Exception {
try {
System.out.println("fanoutQueue2接收到了:"+str);
}catch (Exception e){
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
}
}

输出

fanoutQueue2接收到了:Fanout
fanoutQueue1接收到了:Fanout

4.6 Headers 交换机

代码:https://github.com/majian1994/rabbitMQ_Study

RabbitMQ传输原理、五种模式的更多相关文章

  1. RabbitMQ详解(三)------RabbitMQ的五种模式

    RabbitMQ详解(三)------RabbitMQ的五种模式 1.简单队列(模式) 上一篇文章末尾的实例给出的代码就是简单模式. 一个生产者对应一个消费者!!! pom.xml ​ 必须导入Rab ...

  2. rabbitmq五种模式详解(含实现代码)

    一.五种模式详解 1.简单模式(Queue模式) 当生产端发送消息到交换机,交换机根据消息属性发送到队列,消费者监听绑定队列实现消息的接收和消费逻辑编写.简单模式下,强调的一个队列queue只被一个消 ...

  3. qemu-kvm磁盘读写的缓冲(cache)的五种模式

    qemu-kvm磁盘读写的缓冲(cache)模式一共有五种,分别是writethrough, wirteback, none, unsafe, directsync当你对VM读写磁盘的性能有不同的要求 ...

  4. FTP文件传输协议两种模式 ftp协议集,错误码集,ftp客户端命令集

    TCP/IP协议中,FTP标准命令TCP端口号为21,Port方式数据端口为20.FTP协议的任务是从一台计算机将文件传送到另一台计算机,它与这两台计算机所处的位置.联接的方式.甚至是是否使用相同的操 ...

  5. RabbitMQ 详解 五种队列-SpiritMark

    上次带大家看了一下RabbitMQ的基本概念,今天我们来详解一下 RabbitMQ的五种队列,也算是一个笔记,如果对您有帮助,可以关注一下,便于下次光顾! 文章目录 1.简单队列 2.work 模式 ...

  6. SPI总线传输的4种模式

    概述 在芯片的资料上,有两个非常特殊的寄存器配置位,分别是 CPOL (Clock POlarity)和 CPHA (Clock PHAse). CPOL配置SPI总线的极性 CPHA配置SPI总线的 ...

  7. Rabbitmq的五种模式和案例

    消息生产者p将消息放入队列 消费者监听队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列删除 (缺点:消息可能没有被消费者正确处理,已经消失了,无法恢复) 应用场景:聊天室 1.引入依赖 &l ...

  8. RabbitMQ学习笔记之五种模式及消息确认机制

    本文详细介绍简单模式Simple.工作模式Work.发布订阅模式Publish/Subscribe.Topic.Routing. Maven依赖引用 <dependencies> < ...

  9. TP传输的两种模式

      主动模式(active):   我们知道,FTP是由TCP封包的模式连接,TCP 这种封包由于需要经过 Server 端与 Client 端两边的『三次握手』之后,才能确定联机,也就是需要执行AC ...

随机推荐

  1. PAT A1009-1012

    A 1009 Product of Polynomials (25 point(s)) 读懂题意就行. #include <cstdio> #include <iostream> ...

  2. 基于ssh开发彩票购买系统的设计与实现毕业设计

    开发环境: Windows操作系统开发工具: MyEclipse+Jdk+Tomcat+MYSQL数据库 运行效果图: 源码及原文地址:http://javadao.xyz/forum.php?mod ...

  3. mybatis框架快速入门

    通过快速入门示例,我们发现使用mybatis 是非常容易的一件事情,因为只需要编写 Dao 接口并且按照 mybatis要求编写两个配置文件,就可以实现功能.远比我们之前的jdbc方便多了.(我们使用 ...

  4. [原]调试实战——程序CPU占用率飙升,你知道如何快速定位吗?

    原调试debugwindbghangprocess explorer 前言 如果我们自己的程序的CPU Usage(CPU占用率)飙升,并且居高不下,很有可能陷入了死循环.你知道怎么快速定位并解决吗? ...

  5. HNOI2018/AHOI2018 游戏

    这题放过了暴力其实就没啥意思了 虽然暴力复杂度很玄学,但是思维水平确实没啥 Description link 题意概述:现在有一条长度为 \(n\) 的链,有些边是有限制的 限制为能到某个点,才能经过 ...

  6. Linux 笔记(自用)

    一,常用工具 1. 常用浏览器 w3m links lynx 都可以用 apt-get install *** 安装,访问方式都是 w3m/links/lynx www.baidu.com 的形式 2 ...

  7. 关于Ueditor富文本编辑器的配置和使用心得

    一.环境和项目架构 本文章只是为了便于我个人记录日常笔记,如有错误或缺陷,请指出,文章仅供参考,如有转载请附上本文章链接. 介绍:将Ueditor富文本提交的内容直接生成Html文件,传到后台直接保存 ...

  8. CSP模拟赛2游记

    这次由于有课迟到30min,了所以只考了70min. 调linux配置调了5min,只剩下65min了. T1:有点像标题统计,但要比他坑一点,而且我就被坑了,写了一个for(int i=1;i< ...

  9. Python学习中的随笔,好记性不如烂笔头

    本文 为博主看了 vamei 的blog 写下的随笔 . 致敬Vamei 1.type()   可以显示参数的类型 如 : a=12   type(a) 为 int 2.python的基本类型 为 i ...

  10. 网站的ssl证书即将过期,需要续费证书并更新

    SSL这个证书的续费也挺奇怪,续费跟新购买一样. 证书这个东西,申请成功之后,每次都要重新下载,需要处理好格式之后,放在服务器的指定目录里. 大致操作如下: 首先,申请/续费证书,证书下来后,下载下来 ...