微服务的异步通信技术RabbitMQ
文章目录
- 前言
- 1.WorkQueue(工作队列)
- 消息预取机制
- 2.Publish&Subscribe(发布-订阅)
- 1.Fanout(广播)
- 2.DirectExchange(路由)
- 3.TopicExchange(话题)
- MQ的优点
前言
MQ的出现进一步降低了微服务模块之间的耦合度,相比于同步通信而言减少了关联服务的等待时间,使消息的传递更加多变,灵活
不管什么东西,只要被Spring整合就会变得十分简单,RabbitMQ也不例外
使用SpringAMQP来实现消息收发,不需要重复地配置连接参数,解决了一部分“硬编码”的问题。可以说和MyBatis整合JDBC非常相似。
在以前,使用原生的RabbitMQ收发消息是这样的:

使用SpringAMQP后收发消息是这样的:
这就是一个基本队列(Basic-Queue)

可以看到,只要引入依赖spring-boot-starter-amqp,写好yml配置文件,建立连接、创建通道的工作Spring都为我们做好了,而我们要做的仅仅就是利用工具类发送、监听消息,可以说相当的方便!
针对不同的场景,我们要使用不同的队列模型:
1.WorkQueue(工作队列)

对于单一消费者情况(简单队列),当生产者每秒发送50条消息,消费者每秒处理40条消息,这样每秒钟就会多出10条消息无法处理,由此就会产生生产过剩而导致消息堆积在队列中,一旦达到队列内存的上限,新来的消息就无法被处理而被丢弃。
为了提高消息处理的速度,避免队列中消息的堆积可以将队列绑定多个消费者,即WorkQueue

为了方便观察控制台,一般这样设计:
生产者:
@Test
public void testSendMessage2WorkQueue() throws InterruptedException {String queueName = "work.queue";String message = "hello, MQQ";for (int i = 1; i <= 50; i++) {rabbitTemplate.convertAndSend(queueName, message + i);Thread.sleep(20);}
}
消费者:
为了尽可能的模拟真实场景(消费者处理消息的能力不同),所以设置两个消费者的sleep参数为不同的两个时间
@RabbitListener(queues = "work.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {System.out.println("消费者1接收到消息——【" + msg + "】" + "At "+LocalTime.now());Thread.sleep(20);
}@RabbitListener(queues = "work.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {System.err.println("消费者2接收到消息——【" + msg + "】" + "At "+ LocalTime.now());Thread.sleep(200);
}
消息预取机制
运行后观察控制台,发现所有的消息处理完时间竟然花费了差不多五秒钟,很显然这样的效率是非常低下的:

为何绑定了两个消费者消费消息的速度不快反慢了呢?
仔细观察控制台会发现50条消息是被平均分配的,两个消费者分别消费id为偶数、奇数的消息,这样一想好像是处理能力差的消费者拖了后腿(200msX25=5000ms=5s),为什么会出现这样的情况?
这是由于MQ存在消息预取机制,即消费者会在处理之前预先拿到消息的通道,然后逐个处理消息,这个过程是与处理消息相隔离的!
如果还有人不明白就想想使用原生的RabbitMQ时我们是怎么处理消息的:

当执行完回调函数有可能消息都不会被处理,这时程序会继续向下执行,过段时间才会开始处理消息(其实我认为这也是体现RabbitMQ异步的一个地方)
这样的机制存在是保证异步性的关键,通过人为的设置参数也可以将消息预取的方式做出调整,来保证处理的效率,就像这样:
listener:simple:prefetch: 1
通过prefetch参数来保证消费者每次获取消息的个数,以及处理完成后才能获取下一个批次的消息
进行数据预取设置后消费者在一秒之内处理完了所有的消息:

由此可见对于WorkQueue中消费者的设置要进行“按劳分配”的策略才较为完美
使用工作队列WorkQueue之后处理消息的效率得到了很大的提升,并且也不会出现消息堆积的情况
2.Publish&Subscribe(发布-订阅)
对于简单队列和工作队列模型,生产者发布消息,消费者一旦消费完,消息就会被销毁。这样无法做到将一个消息同时发送给多个消费者。

对于一个微服务项目,在支付订单的模型中当支付服务完成,会发消息同时去通知短信服务、订单服务…这就需要保证消息的高可用,不能一个服务消费完消息就被销毁而导致其他服务接收不到消息
如何做到将同一消息发送给多个消费者并让其各自接收到?采用发布&订阅的工作模型即可

通过交换机(exchange)将消息路由到不同的队列中,再由消费者来消费各自订阅队列中的消息
针对不同的交换机种类会有不同的发布策略:
1.Fanout(广播)
SpringAMQPA提供了声明交换机、队列、绑定关系的API

所以使用Exchange接口下的实现类就可以实现将消息路由到每一个绑定的Queue中,使用代码就会变得非常简单,声明交换机并绑定队列即可:
@Bean
public FanoutExchange fanoutExchange(){return new FanoutExchange("yu7.fanout");
}// fanout.queue1
@Bean
public Queue fanoutQueue1(){return new Queue("fanout.queue1");
}// 绑定队列1到交换机
@Bean
public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange){return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
}// fanout.queue2
@Bean
public Queue fanoutQueue2(){return new Queue("fanout.queue2");
}// 绑定队列2到交换机
@Bean
public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange){return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}

注意:交换机只能用作消息的转发路由,不能用作消息的存储,一旦路由失败消息就会丢失!
2.DirectExchange(路由)
DirectExchange会将接收到的消息根据规则路由到指定的Queue中,生产者发布消息时指定消息的RoutingKey与消费者声明的bindingKey相匹配,从而达到“精确制导”

为了简化开发不使用声明Bean的方式来完成配置,通过@RabbitListener注解即可一键完成,所以根本不需要使用配置类:
生产者:
@Test
public void testSendDirectExchange() {// 交换机名称String exchangeName = "yu7.direct";// 消息String message = "hello, MQ!";// 发送消息rabbitTemplate.convertAndSend(exchangeName, "A", message);
}
消费者:
@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue1"),exchange = @Exchange(name = "yu7.direct", type = ExchangeTypes.DIRECT),key = {"A", "B"}
))
public void listenDirectQueue1(String msg){System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
}@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue2"),exchange = @Exchange(name = "yu7.direct", type = ExchangeTypes.DIRECT),key = {"A", "C"}
))
public void listenDirectQueue2(String msg){System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
}

当队列的bindingKey都相同时就变成了广播模型
3.TopicExchange(话题)
Topic与Direct非常相似,他允许RoutingKey-BindingKey以通配符的形式进行匹配,这样就可以更加有针对性的路由、订阅更多消息,并且以前用多个BindingKey的情况现在只需要用一个就能解决:
#:代表0或者多个单词
*:代指一个单词

MQ的优点
1、耦合度低:每次有新需求,只需要添加对应的订阅即可
2、吞吐量提升:各自处理自己订阅的事件,不需要等待执行完毕后再释放资源
3、故障隔离:因为没有强依赖,中间某一环节出了问题,不会影响整个流程
4、流量削峰:MQ就像—根管道,大量请求来了,你们给我排好队,依次执行
相关文章:
微服务的异步通信技术RabbitMQ
文章目录前言1.WorkQueue(工作队列)消息预取机制2.Publish&Subscribe(发布-订阅)1.Fanout(广播)2.DirectExchange(路由)3.TopicExchange(话题)MQ的优点前…...
Word处理控件Aspose.Words功能演示:使用 C++ 在 Word (DOC/DOCX) 中添加或删除水印
Aspose.Words 是一种高级Word文档处理API,用于执行各种文档管理和操作任务。API支持生成,修改,转换,呈现和打印文档,而无需在跨平台应用程序中直接使用Microsoft Word。此外, Aspose API支持流行文件格式处…...
chatGPT模型原理
文章目录简介BertGPT 初代GPT-2GPT-3chatGPT开源ChatGPT简介 openai 的 GPT 大模型的发展历程。 Bert 2018年,自然语言处理 NLP 领域也步入了 LLM 时代,谷歌出品的 Bert 模型横空出世,碾压了以往的所有模型,直接在各种NLP的建模…...
四、阻塞队列
文章目录基础概念生产者消费者概念JUC阻塞队列的存取方法ArrayBlockingQueueArrayBlockingQueue的基本使用生产者方法实现原理ArrayBlockingQueue的常见属性add方法实现offer方法实现offer(time,unit)方法put方法消费者方法实现原理remove方法poll方法poll(time,unit)方法take方…...
企业电子招投标采购系统源码之登录页面
信息数智化招采系统 服务框架:Spring Cloud、Spring Boot2、Mybatis、OAuth2、Security 前端架构:VUE、Uniapp、Layui、Bootstrap、H5、CSS3 涉及技术:Eureka、Config、Zuul、OAuth2、Security、OSS、Turbine、Zipkin、Feign、Monitor、…...
SQL零基础入门学习(十三)
上一篇(SQL零基础入门学习(十二)) SQL 视图(Views) 视图是可视化的表。 SQL CREATE VIEW 语句 在 SQL 中,视图是基于 SQL 语句的结果集的可视化的表。 视图包含行和列,就像一个…...
Java实现简单KV数据库
用Java实现一个简单的KV数据库 开发思路: 用map存储数据,再用一个List记录操作日志,开一个新线程将List中的操作写入日志文件中,再开一个线程用于网络IO服务接收客户端的命令,再启动时检查日志,如果有数据就…...
【Spark分布式内存计算框架——Spark Streaming】5. DStream(上)
3. DStream SparkStreaming模块将流式数据封装的数据结构:DStream(Discretized Stream,离散化数据流,连续不断的数据流),代表持续性的数据流和经过各种Spark算子操作后的结果数据流。 3.1 DStream 是什么…...
Spring系列-9 Async注解使用与原理
背景: 本文作为Spring系列的第九篇,介绍Async注解的使用、注意事项和实现原理,原理部分会结合Spring框架代码进行。 本文可以和Spring系列-8 AOP原理进行比较阅读 1.使用方式 Async一般注解在方法上,用于实现方法的异步…...
Python自动化测试实战篇(6)用PO分层模式及思想,优化unittest+ddt+yaml+request登录接口自动化测试
这些是之前的文章,里面有一些基础的知识点在前面由于前面已经有写过,所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇(1)读取xlsx中账户密码,unittest框架实现通过requests接口post登录网站请求&…...
Linux 进程:父子进程
目录一、了解子进程二、创建子进程1.创建子进程2.区分父子进程三、理解子进程四、创建子进程的意义进程就是运行中的应用程序,如果一个程序较为庞大,我们可以给这个程序创建多个进程,每个进程负责一部分代码的运行。 A进程如果创建了B进程&am…...
Unity 之 实现读取代码写进Word文档功能实现 -- 软著脚本生成工具
Unity 之 实现读取代码写进Word文档功能前言一,实现步骤1.1 逻辑梳理1.2 用到工具二,实现读写文件2.1 读取目录相关2.2 读写文件三,编辑器拓展3.1 编辑器拓展介绍3.2 实现界面可视化四,源码分享4.1 工具目录4.2 完整代码前言 之所…...
Typora图床配置:Typora + PicGo + 阿里云OSS
文章目录一、前景提要二、相关链接三、搭建步骤1. 购买阿里云对象存储OSS2. 对象存储OSS:创建Bucket3. 阿里云:添加OSS访问用户及权限4. 安装Typora5. 配置PicGo方法一:使用PicGo-Core (Command line)方法二:使用PicGo(app)6. 最后…...
二进制搭建以太坊2.0节点-2023最新详细版文档
文章目录 一、配置 JWT 认证二、部署执行节点geth2.1 下载geth二进制文件2.2 geth节点启动三、部署共识节点Prysm3.1 下载Prysm脚本3.2 Prysm容器生成四、检查节点是否同步完成4.1 检查geth执行节点4.2 检查prysm共识节点4.3 geth常用命令五、节点同步详细说明5.1 启动时日志5.…...
如何简化跨网络安全域的文件发送流程,大幅降低IT人员工作量?
为什么要做安全域的隔离? 随着企业数字化转型的逐步深入,企业投入了大量资源进行信息系统建设,信息化程度日益提升。在这一过程中,企业也越来越重视核心数据资产的保护,数据资产的安全防护成为企业面临的重大挑战。 …...
带你深入了解c语言指针后续
前言 🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏: 🍔🍟🌯 c语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介:>:介绍c语言中有关指针更深层的知识. 金句分享: ✨在该…...
借助Intune无感知开启Bitlocker
希望使用 Intune 部署 BitLocker,但不知道从哪里开始?这是人们最开始使用 Intune 时最常见的问题之一。在本博客中,你将了解有关使用 Intune 管理 BitLocker 的所有信息,包括建议的设置、BitLocker CSP 在客户端上的工作方式&…...
零基础该如何转行Python工程师?学习路线是什么?
最近1年的主要学习时间,都投资到了 python 数据分析和数据挖掘上面来了,虽然经验并不是十分丰富,但希望也能把自己的经验分享下,最近也好多朋友给我留言,和我聊天,问我python该如何学习,才能少走…...
Go项目(商品微服务-1)
文章目录简介建表protohandler商品小结简介 商品微服务主要在于表的设计,建哪些表?表之间的关系是怎样的? 主要代码就是 CURD表和字段的设计是一个比较有挑战性的工作,比较难说清楚,也需要经验的积累,这里…...
机器学习——集成学习
引言 集成学习:让机器学习效果更好,单个不行,群殴走起。 分类 1. Bagging:训练多个分类器取平均(m代表树的个数)。 2.Boosting(提升算法):从弱学习器开始加,通过加权来进行训练。…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
