本文代码基于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. h5-拖拽接口

    1.原效果网页 拖拽后: 2.主要实现代码 <div class="div1" id="div1"> <!--在h5中,如果想拖拽元素,久必须 ...

  2. 用户交互Scanner

    用户交互Scanner java.util.Scanner Scanner类可以获取用户的输入. Java 5 通过Scanner类的next()和nextLine()方法获取输入的字符串 在读取前我 ...

  3. 卡常的编译命令(含O2优化)

    不解释,直接来 //包括O2,O3之类的编译命令 //直接copy and paste #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma ...

  4. java网络考试系统的设计与实现 jsp 源码

    开发环境: Windows操作系统开发工具:MyEclipse/Eclipse + JDK+ Tomcat + MySQL 数据库 项目简介: 网络考试系统主要用于实现高校在线考试,基本功能包括:自动 ...

  5. urllib简单介绍

    # urllib简介: 1.urllib模块是Python的一个请求模块 2.Python2中是urllib和urllib2相结合实现请求的发送. Python3中统一为urllib库 3.urlli ...

  6. Activiti工作流的入门介绍

    一.activiti介绍 Activiti5是一个 业务流程管理(BPM)框架,它是覆盖了业务流程管理.工作流.服务协作等领域的一个开源的.灵活的.易扩展的可执行流程语言框架.Activiti基于Ap ...

  7. webpack--删除dist目录

    1.安装clean-webpack-plugin插件 npm install clean-webpack-plugin --D 2.在webpack.dev.conf.js或者webpack.conf ...

  8. SQL查询出一个表数据插入到另一个表里

    下面两中方式都是将 srcTbl 的数据插入到 destTbl,但两句又有区别的: 方式一 (select into from)要求目标表(destTbl)不存在,因为在插入时会自动创建. selec ...

  9. 整理平时常用git命令

    git常用命令 git创建分支 #创建本地分支并切换到新创建的分支 $ git checkout -b newbranch #将新创建的分支信息推送到github $ git push origin ...

  10. 输入一段汉字可以获得首字母简拼的java代码

    package com.zl; import java.io.UnsupportedEncodingException; public class Test12 { public static voi ...