RabbitMQ传输原理、五种模式
本文代码基于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传输原理、五种模式的更多相关文章
- RabbitMQ详解(三)------RabbitMQ的五种模式
RabbitMQ详解(三)------RabbitMQ的五种模式 1.简单队列(模式) 上一篇文章末尾的实例给出的代码就是简单模式. 一个生产者对应一个消费者!!! pom.xml 必须导入Rab ...
- rabbitmq五种模式详解(含实现代码)
一.五种模式详解 1.简单模式(Queue模式) 当生产端发送消息到交换机,交换机根据消息属性发送到队列,消费者监听绑定队列实现消息的接收和消费逻辑编写.简单模式下,强调的一个队列queue只被一个消 ...
- qemu-kvm磁盘读写的缓冲(cache)的五种模式
qemu-kvm磁盘读写的缓冲(cache)模式一共有五种,分别是writethrough, wirteback, none, unsafe, directsync当你对VM读写磁盘的性能有不同的要求 ...
- FTP文件传输协议两种模式 ftp协议集,错误码集,ftp客户端命令集
TCP/IP协议中,FTP标准命令TCP端口号为21,Port方式数据端口为20.FTP协议的任务是从一台计算机将文件传送到另一台计算机,它与这两台计算机所处的位置.联接的方式.甚至是是否使用相同的操 ...
- RabbitMQ 详解 五种队列-SpiritMark
上次带大家看了一下RabbitMQ的基本概念,今天我们来详解一下 RabbitMQ的五种队列,也算是一个笔记,如果对您有帮助,可以关注一下,便于下次光顾! 文章目录 1.简单队列 2.work 模式 ...
- SPI总线传输的4种模式
概述 在芯片的资料上,有两个非常特殊的寄存器配置位,分别是 CPOL (Clock POlarity)和 CPHA (Clock PHAse). CPOL配置SPI总线的极性 CPHA配置SPI总线的 ...
- Rabbitmq的五种模式和案例
消息生产者p将消息放入队列 消费者监听队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列删除 (缺点:消息可能没有被消费者正确处理,已经消失了,无法恢复) 应用场景:聊天室 1.引入依赖 &l ...
- RabbitMQ学习笔记之五种模式及消息确认机制
本文详细介绍简单模式Simple.工作模式Work.发布订阅模式Publish/Subscribe.Topic.Routing. Maven依赖引用 <dependencies> < ...
- TP传输的两种模式
主动模式(active): 我们知道,FTP是由TCP封包的模式连接,TCP 这种封包由于需要经过 Server 端与 Client 端两边的『三次握手』之后,才能确定联机,也就是需要执行AC ...
随机推荐
- bash字符串处理
将movie目录下的文件名写到markdown文件中 , 再转html rm index.md ; for f in `find . *.* | sort`; do [ -f $f ] &&a ...
- qt使用了qstackedwidget里面放置了widget后对该子widget设置的样式无效
关键字:子窗口样式无效 QStackedwidget 问题: 我有一个对话框,里面放了一个qstackedwidget,qstackedwidget放了N个子窗口,使用addwidget添加上去了: ...
- String 字符串拼接
字符串拼接有两个方法 第一中 var sad = "happy" var variable = "you"+sad +"" variable ...
- GaussDB数据dump实现完全同步
问题背景:搭建服务后端容灾集群,服务正常时容灾DB需要从业务DB完全同步数据,服务异常时,容灾DB停止抽取数据,自动从探针采集业务数据. 解决方案:常用的有两种思路,一是从服务后端定时每天拉取业务DB ...
- 从定时器的选型,到透过源码看XXL-Job(上)
此内容来自一位好朋友的分享,也是当初建议我写博客提升的朋友.内容只做转载,未做修改. 定时任务选型 背景 目前项目定时任务采用Spring Task实现,随着项目需求的迭代,新增的定时任务也越来越多. ...
- shiro的小白学习
1. shiro是啥就不用说了吧 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理 SecurityManager 是shiro的核心.它不同于java. ...
- 01 语言基础+高级:1-2 面向对象和封装_day06【类与对象、封装、构造方法】
day06[类与对象.封装.构造方法] 面向对象类与对象三大特征——封装构造方法 能够理解面向对象的思想能够明确类与对象关系能够掌握类的定义格式能够掌握创建对象格式,并访问类中的成员能够完成手机类的练 ...
- List和Map集合详细分析
1.Java集合主要三种类型(两部分): 第一部分:Collection(存单个数据,只能存取引用类型) (1).List :是一个有序集合,可以放重复的数据:(存顺序和取顺序相同) (2).Set ...
- 大言不惭 swank? talk about sth or speak too confidently cán,意思是指说大话而毫不感到难为情。出自《论语·宪问》:“子曰:‘其言之不怍,则为之也难。’”宋·朱熹注:“大言不惭,则无必为之志,而不自度其能否也。欲践其言,其不难哉!” 是不是类似于 swank?
大言不惭 swank? talk about sth or speak too confidently cán,意思是指说大话而毫不感到难为情.出自<论语·宪问>:“子曰:‘其言之不怍,则 ...
- Python字典基础