RabbitMQ的五大常用模型介绍以及SpringAMQP的使用

news2024/11/27 3:42:43

目录

    • MQ的介绍
      • 使用MQ的好处
      • 技术对比
    • RabbitMQ的使用
      • Docker启动
      • MQ角色介绍
      • 五大常用消息模型
      • 使用SpringAMQP模板
        • 简单队列模型
        • 工作队列模型
        • 广播模型 Fanout
        • 路由模式 Direct
        • 主题模式 Topic

MQ的介绍

MQ,MessageQuene即 消息队列,是程序与程序之间的异步通信一种方法。
为什么使用MQ?
MQ用来做程序间异步通信的,为什么使用MQ问题也是为什么要引入程序间异步通讯问题。
什么是同步通讯?
同步通讯简单理解就是下一行要执行的代码要等到上一行完全执行完毕才能够执行。例如:我要根据订单Id查询到用户信息并返回。此时就要先通过id调用订单系统查询到订单信息,在从订单信息中获取用户id,调用用户系统传递用户id去查询到用户信息并返回。
要想查询到用户信息就一定先查到订单信息,下一行代码执行依赖于上一行代码的执行结果,我们通常在代码中使用到就是同步通讯,从上到下是一步步执行的,不可以跳跃。就像当给axios加上async一样。
一个使用同步通讯不合理的例子:下单支付后,下单系统会等待支付完成,再调用仓储系统,进行扣除库存等操作,等到仓储系统全部执行成功后,在交给物流系统,等到物流系统全部执行成功后,再返回给用户下单成功信息。
同步通讯:各个系统像链一样执行,有一处断则全断,添加、删除就要修改
在这里插入图片描述
异步通讯:下一行代码不必等待上一行代码的执行结果,只要确保其被调用,最终会执行即可。类似于子线程、axios等
在这里插入图片描述
通过此例子中同步和异步通讯对比,就可以发现同步带来的问题?

  • 性能和吞吐能力下降:用户要等到这些系统一个个全部按顺序全部执行成功后,才能得到响应结果。使得用户的体验,大大的下降。就像去饭店点餐一样,只要顾客说出要吃的菜后,服务员就可以告诉顾客点菜成功(虽然没有做,但可以一定能够成功),而不是没有反应等到菜端上来后,菜告诉用户点菜成功。这样就会使得用户感觉点菜花的时间过长。回到上一个例子:在用户已经支付后,就已经能够确保下单一定成功了,返回给用户响应信息。其余的操作交给后台就可以了,只要保证成功即可,而不必让用户去等待。

  • 资源浪费:在用户支付成功后,不仅没有及时的返回用户结果,此线程还要参与调用其他系统,造成此线程资源的浪费。

  • 耦合度高:如果在仓储系统与物流系统中加上一个短信系统,那么这个下单系统的代码就要发生修改,在整个代码结果中塞进去一个短信系统。而使用异步通讯,只需要将一个独立的短信系统开发后,接入中间件,监听来自下单系统的统一发送的通知即可。
    调用者只负责将信息发送到队列中,被调用者只负责从消息队列中取出消息进行调用,二者通过这个中间人(消息队列)实现代码解耦。
    在这里插入图片描述
    级联失败:同步通讯中,在此过程如果发生一个错误,那么下面代码就不会执行,整个业务就全发生错误。但使用异步通讯,业务之间都是通过中间件获取消息的,彼此之间没有联系。即使有一个业务执行失败了,也不会影响到其他业务。

总结
使用异步通讯,能够快速的对用户操作做出反应(虽然没有执行完,但能确保最终执行成功),释放掉资源用给下一个用户。异步服务间相对独立,都统一受中间件的调用,要添加,删除某个服务十分的方便,同时,当有服务发生故障时,也不会影响到其他服务。

既然异步通信这么好,为什么还要使用同步通讯?
还是上一个例子,下单系统中一定要同步调用支付系统,因为后续的调用都在等在支付结果。如果要使用异步通讯,最后还要在代码的下面等待结果,就可有能还不如同步通讯调用这样有更高的时效性

怎样实现异步通讯?
如果是一个服务,可以开启一个子线程去调用即可,但这样只能使得要用户的响应速度得到提升。那如果是很多个服务呢?服务动态伸缩呢?这就需要一个消息队列。
通常就是将调用的消息发送到一个队列中,各个被调用者各自监听这个队列中的消息,队列中一有消息,就从消息队列中获取参数执行对应的方法。

使用MQ的好处

解耦:服务调用之间可以达到插拔式效果,各个服务间彼此隔离,互不影响
异步:无需等待后续处理,提升了系统吞吐量,提升响应速度,减少阻塞
削峰:大流量由消息队列先挡在各个服务的前面,消息可以在队列中留存作为缓冲,队列以合适的速度去调用
就像一个水坝一样,当有大的洪水时,水坝可以将洪水留在蓄水池中,然后以合适的速度下放,起到一个缓冲的作用。

使用MQ的坏处

  • 系统复杂度提高:像重复消费问题,顺序问题,消息可靠性问题,死信问题等等又多了一系列问题。业务流程相比于同步调用较为混乱。
  • 可用性问题:引入了MQ,就要保证MQ能够正常可用。异步调用依赖于MQ的性能,安全性,可用性。 多了一个风险
  • 一致性问题

注意:不是MQ去调用各个服务的,是各个服务监听MQ的消息通过回调去调用对应的方法
思考

  • 服务间的调用不是通过服务注册中心吗?通过服务中心发起Ribbon调用是同步调用
  • 使用MQ怎样达到一个服务某个实例的调用?多个服务某个实例的调用?

技术对比

常见的MQ有Rabbit MQ,Rocket MQ,Active MQ,Kafka。这些技术里Active MQ较弱

RabbitMQActiveMQRocketMQKafka
公司/社区RabbitApache阿里Apache
开发语言ErlangJavaJavaScala&Java
协议支持AMQP,XMPP,SMTP,STOMPOpenWire,STOMP,REST,XMPP,AMQP自定义协议自定义协议
可用性一般
单机吞吐量一般非常高
消息延迟微秒级毫秒级毫秒级毫秒以内
消息可靠性一般一般

RabbitMQ和RocketMQ是消息延迟吞吐量的取舍,kafka最大亮点高吞吐,通常都是在大数据中使用的。

RabbitMQ的使用

Docker启动

使用命令

docker run \
 -e RABBITMQ_DEFAULT_USER=用户名 \
 -e RABBITMQ_DEFAULT_PASS=密码 \
 --name mq \
 --hostname myhost \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 Rabbit镜像id

RabbitMQ暴露两个端口,15672是用来供浏览器访问提供可视化管理的,5672是用来被各个服务连接的。
通过浏览器访问15672,输入配置的用户名,密码即可来到管理页面
在这里插入图片描述

管理面板有很多的功能,不止可以用于查看,例如:向队列中添加消息,查看队列消息,添加队列,管理队列,添加交换机,管理交换机等。

MQ角色介绍

在这里插入图片描述
MQ最基本的就是图中四大个角色
publisher:消息发布者,就是各个服务的调用者,就是它将消息发送到消息队列,其他服务(下面的消费者)监听回调
exchange:交换机,负责将消息广播到各个队列。给我的感觉就像凹透镜发散光一样,将消息发布者的消息广播到订阅的队列中,注意是同一个消息送到指定队列。如果发布者只将消息发送到一个队列中,则无需交换机。有交换机就与队列存在发布与订阅关系。交换机不能缓存数据,发送到队列过程失败,消息直接丢失。
quene:消息队列,有推送消息到消费者和暂存消息的功能。队列中的消息只能被一个消费者消费。
consumer:消息消费者,消费者一定是要从队列中获取消息的,然后在其内部监听队列中消息,监听的队列中一有消息就进行回调,达到调用服务中的方法目的。

一个服务使用一个队列,服务中的各个实例作为一个队列下的消费者。

五大常用消息模型

生产者也可以有多个,但在模型中,一个和多个只要以相同的消息格式往一个队列里发是一样的,为了简化用1个实例去代表
没有交换机
没有交换机,适用于消费者是一个服务下有多或一个实例,各个实例间争抢消费同一个消息。

  • 1、基本消息队列:一个队列一个消费者,相当于一个服务下只有一个实例
    在这里插入图片描述
  • 2、工作消息队列:一个队列对应多个消费者,相当于一个服务下有多个实例
    在这里插入图片描述

有交换机
交换机可以将一个消息发送到各个队列中,用于将调用信息发送到各个服务对应的某个实例下
但那些队列能够获取交换机的消息呢?常见的有以下三种

  • 广播 Fanout:只要是在一个主机下的所有队列都将获取到此交换机的信息,相当于调用此主机下所有服务中某个实例
    在这里插入图片描述

  • 路由 Direct:只有完美匹配对应上的队列才能够接收到消息
    在这里插入图片描述

  • 主题 Topic:只有匹配对应上的队列才能够接收到消息,与上一个不同的是,匹配的不要求一一对应,可以是一个范围,一个.级别

在这里插入图片描述

思考:当一个RabbitMQ服务器中不止这一组服务发布者与消费者怎么办?也就是说,多个相同的不同业务队列模型怎样进行隔离呢?
RabbitMQ中引入了一个VirtualHost,通过这个达到多租户效果,即一个RabbitMQ在逻辑上被分为多个小RabbitMQ,VirtualHost之间做到完全隔离。
因此,在连接时不仅要指定RabbitMQ的ip和端口,还要具体到是哪一个VirtualHost
在这里插入图片描述

使用SpringAMQP模板

什么是AMQP :AMQP,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。

SpringAMQP 在代码中简化RabbitMQ的使用

  1. 要在生产者与服务者之间引入依赖
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  1. 在配置文件中指明RabbitMQ服务的ip和端口以及对应的VirtualHost(默认为/
spring:
  rabbitmq:
    host: 192.168.83.100 
    port: 5672
    virtual-host: /
    username: yan
    password: 1234

在模拟演示过程,消费者端将监听到的消息直接进行输出,在实际应用中,是根据消息作为参数利用回调调用此服务方法实现异步调用的。

简单队列模型

在简单消息模型中,发送一个消息需要知道 队列名字,消费者根据队列名字来确定监听这个队列

  • 生产者

  • 生产者通常会在service业务中向队列中发送消息,controller调用service。这里在为了简便直接在controller中编写业务
    在这里插入图片描述

  • 消费者
    在这里插入图片描述

值得注意的是,这种方式需要声明队列
在这里插入图片描述

工作队列模型

即一个队列,多个消费者

在这里插入图片描述
那么一个队列中有多个消费者,消息是如何分配的呢?一个消费者消费完消息另一个会不会再次消费呢?
例:当队列中有10个消息,有消费者A,B都监听这个队列。消费者是先将消息平均取出,再进行消费的。一个消息被取出,其他消费者就不能消费此消息。
先平均取出再进行消费就存在一个问题:,性能差的和性能好的取出的一样多,但一样数量的消息消费起来时间就不一样了,就会发生性能好的消费者空余,性能差的消费者处理不过来。整体的服务处理效率就会降低,应该让他们按处理能力去取消息。
如果使其按处理能力取出呢?
就好比吃自助餐,饭量小的和饭量大的拿的一样多,要使其按饭量去取,就要让他们吃完再去取。消费队列也是一样,每次每个消费者只能拿1个消息(可以配置个数),处理过后,再去取下一个消息,虽然取的次数变多了,但能者多劳,分配的更加合理,整体的处理效率就提高了。
修改配置:
在这里插入图片描述

广播模型 Fanout

引入了交换机向各个队列中扩散消息
在这里插入图片描述
消费者依然指定队列名从队列中获取消息,但与生产者直接相连的不在是队列,而是交换机。发布者向交换机中发送信息,交换机在给绑定的队列广播信息
因此在生产者端就要声明交换机,声明队列,绑定交换机与队列最后将消息发送给交换机

这里使用名字为fan1的交换机 使用队列名为q1,q2的两个队列与此交换机进行绑定,每个队列下都有一个消费者。我向交换机中发送一条消息,两个队列下的消费者都会接收到消息

方式一:使用@Bean方式 将队列与交换机进行绑定


消费者端
在这里插入图片描述

依旧是从队列中获取消息
在这里插入图片描述
生产者端
生产者依旧是向队列发送消息
在这里插入图片描述

流程总结
在这里插入图片描述
方式二:使用注解的方式
生产者依旧是将消息发送给交换机,但它不必去管理队列和绑定交换机与队列
消费者在监听队列时,指明其交换机并且绑定队列与交换机的关系

在这里插入图片描述

交换机总结

  1. 有了交换机后,生产者是要将消息发送给交换机的
  2. 交换机要与队列进行绑定

注意
3. 交换机不能缓存数据,交换机向队列广播消息过程失败则消息丢失

路由模式 Direct

与广播模式不同的是,并不是所有与交换机绑定的队列就一定能够获取交换机推送的消息,还要在此基础上,口令(routing Key)与交换机当前消息的口令(routing Key)完全匹配。这种模式能够使得交换机将消息广播到与它绑定的队列中,指定的某些队列上。
例如:我有在订单系统本次发送消息只想调用仓储系统,但与交换机绑定的还有物流系统,这就需要在消息中指明routingKey,只有与之匹配的队列才能够获取,使得调用更加灵活。

使用方式在Faout模式下,生产者端在发送消息时指定routingKey
在这里插入图片描述

消费者端在绑定队列和交换机时指定routingKey
在这里插入图片描述

在DIRCT模式中,队列要想获取交换机中的数据满足的条件

  1. 与交换机进行绑定
  2. 队列的routingKey中包含与之绑定交换机的此次消息的routingKey

即使routingKey相同,监听队列绑定的交换机不同,也无法获取数据
Direct模式当各个队列的routingKey都含有此消息的routingKey,那么就达到了Faout模式

主题模式 Topic

这种模式相较于Direct模式,routingKey可以使用通配符。每一个.代表隔着级别,*代表一个级别,#代表任意级别
例如:a.b.c =a.*.c=a.#a.b.c!=a.*因为*只能代表一级(一个单词)
注意,这里*代表的是少的,只能代表一个

这种通配符既可以在生产者的消息中指定,又可以在消费者绑定队列中指定
生产者
在这里插入图片描述
消费者
在这里插入图片描述
也可以使用@Bean的方式声明和绑定,这里只演示一个
在这里插入图片描述

注意:不仅可以向队列发送String类型的消息,所有类型的均支持。在发送对象作为消息时,由于对象的传输一定需要进行序列化。

那么怎样通过消息队列传递对象数据呢?
可以发送方可以将对象转为JSON字符串格式将对象序列化,消费者端可以将JSON字符串进行反序列化转为对象。

springAMQP虽然能够自动创建交换机,队列,但生产者向队列,交换机发送前前一定要确保已经存在。声明可以在消费者端,也可以在生产者端,但应该在消费者端,这样当有业务增减时对生产者可以实现无感知的。队列、交换机以及他们绑定关系的声明可以通过注解这种方式声明,也可以通过向容器中注入bean的方式声明

RabbitMQ中一但创建了交换机指定了类型是不允许修改的,除非删掉这个虚拟机
例如:我创建了一个交换机名为a1,指定为faout类型,但我想要将a1更换为direct,这时是不成功的,a1交换机仍为faout类型,而且会报这样的错误inequivalent arg 'type' for exchange 'e1' in vhost '/': received 'direct' but current is 'fanout'
解决方式要么换一个交换机的名字,要么删除重新创建新的类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring的IOC与DI小案例

注&#xff1a;本博客用于自学&#xff0c;如有错误&#xff0c;敬请指正。 一、首先要了解的概念 IOC是一种思想&#xff1a;将创建、初始化对象的控制权交给IOC容器 IOC容器&#xff1a;用来装对象的容器 Bean&#xff1a;IOC容器中装的对象 DI&#xff1a;将IOC容器种Bean…

打破光缆资源管理难题

如何打破光缆资源管理难题&#xff0c;将光缆资源价值最大化&#xff0c;相信是每个运维人都关心的焦点问题。 一、背景 全业务发展到今天&#xff0c;我们逐渐意识到&#xff0c;市场竞争的核心已聚焦到传输资源。从拼资源总量到拼资源管理&#xff0c;传统的运维管理模式面临…

【每日一题】【LeetCode】【第九天】存在重复元素

解决之路 题目描述 测试案例&#xff08;部分&#xff09; 第一次 关键就是看是否有重复元素&#xff0c;暴力解法就是双循环&#xff0c;然后有相同元素就返回True&#xff0c;否则就等循环结束&#xff0c;返回False。 但是自己想到了另一个只需要一个循环的解法。用一个…

C++11 简化 “策略模式” 的一种思路

策略模式是一种定义一系列算法的模式&#xff0c;从概念上看&#xff0c;所有这些算法完成的都是相同接口的工作&#xff08;只是实现不同&#xff09;&#xff0c;它可以以相同的方式调用所有的算法&#xff0c;减少了各种算法类与使用算法的类之间的耦合。实践中只要在分析过…

开发者工具中performance面板解析

目录总览设置面板网络性能总览图火焰图&#xff1a;耗费的时间相关信息总览 如图&#xff0c;主要分为以下4个方面 设置面板 1&#xff0c; 不刷新页面进行录制 2&#xff0c;刷新页面进行录制 3&#xff0c;删除性能展示页面 4&#xff0c;上传本地的性能页面 5&#xff0c…

若依mybatis升级mybatis-plus,其他也适用

若依mybatis升级mybatis-plus,其他也适用 本文档记录若依mybatis升级plus,其他也类似 文章目录若依mybatis升级mybatis-plus,其他也适用第一步 修改ruoyi-common的pom.xml第二步 修改ruoyi-admin的application.yml第三步 修改ruoyi-framework的MyBatisConfig.java开始测试1. 修…

一、计算机系统概论

文章目录一、计算机系统简介1.计算机软硬件概念2.计算机系统的层次结构&#xff08;1&#xff09;现代计算机&#xff08;2&#xff09;发展&#xff08;3&#xff09;软硬件分布3.计算机组成和计算机体系结构二、计算机的基本组成1.冯诺依曼计算器2.计算机硬件框图3.计算机工作…

android架构拆分方案-编译相关方案与技术

接上文https://blog.csdn.net/dongyi1988/article/details/128617738直接拆分那么在代码和做包过程中&#xff0c;需要用到分仓&#xff0c;super.img、boot、OTA升级包的拆分与组合&#xff0c;vendor与system相关依赖处理&#xff0c;开机验证链还必须给镜像签名。代码管理定…

VisualODX——ODX数据自动转换工具

在创建ODX数据库的过程中&#xff0c;我们需要录入大量的数据以及应对多种数据格式。这不仅费时费力&#xff0c;而且还需要很高的人力成本&#xff0c;并且其错误率也高&#xff0c;从而导致了效率低下、开发速度缓慢。基于多年的汽车行业诊断经验&#xff0c;Softing开发了Vi…

基于微信小程序的课程分享平台小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

xv6-lab3: page table

文章目录1.speed up system call2. print page3. Detecting which pages have been accessedwhere is page table?function of page table?how to get arguments from user to kernel? – reference:https://stackoverflow.com/questions/46870509/how-to-pass-a-value-into…

风速记录仪大屏幕液晶显示全中文操作菜单操作简单

风速记录仪功能特点◆本机体积小巧美观&#xff0c;大屏幕液晶显示&#xff0c;全中文操作菜单&#xff0c;操作简单&#xff0c;性能可靠&#xff0c;记录间隔可根据要求从1分至24小时任意设置。&#xff08;但由于存储器空间有限&#xff0c;设置后也可随时查看存储信息&…

【阶段三】Python机器学习09篇:机器学习项目实战:决策树回归模型

本篇的思维导图: 项目背景 决策树除了能进行分类分析,还能进行回归分析,即预测连续变量,此时的决策树称为回归决策树。回归问题是一类预测连续值的问题,而能满足这样要求的数学模型称作回归模型,本项目应用决策树回归模型进行探索新冠疫情、原材料、人工、物流等因素对…

generalized focal loss之quality focal loss(附代码)

参见paper 其中包含有Quality Focal Loss 和 Distribution Focal Loss。 先来说一下Quality Focal Loss, 在这之前&#xff0c;先要了解一下Focal Loss, 在这篇文章里有写过。 它主要是解决class imbalance&#xff0c;同时降低容易分类的weight&#xff0c;使训练更集中到难…

集成开发工具IDEA导入新项目,update index时间过程

集成开发工具IDEA导入新项目&#xff0c;update index时间过程&#xff0c;一般这种场景出现在&#xff1a; 1.继承开发工具比较新&#xff08;使用了优化的索引算法&#xff0c;这种算法一般是前期编译比较慢&#xff0c;一旦编译完&#xff0c;存入缓存&#xff0c;后期搜索就…

C语言经典100例(008,009,010)

题目&#xff1a;输出9*9口诀。 程序分析&#xff1a;分行与列考虑&#xff0c;共 9 行 9 列&#xff0c;i 控制行&#xff0c;j 控制列。 程序源代码&#xff1a; #include<stdio.h> int main() {int i,j,result;printf("\n");for (i1;i<10;i){for(j1;j…

fork()||fork(),循环fork()与\n

fork()||fork()输出几个A&#xff1f;父进程左边的fork()产生一个子进程&#xff08;1&#xff09;&#xff0c;父进程fork返回值>0,||不执行右边。子进程&#xff08;1&#xff09;中fork()返回0&#xff0c;执行||右边的fork()产生第二个子进程&#xff08;2&#xff09;到…

电脑蓝屏怎么解决?一键快速解决蓝屏问题

电脑蓝屏也是一个经常出现的情况&#xff0c;让人十分头疼。蓝屏的原因有很多&#xff0c;但大多数是由内存条、硬盘或散热系统造成的。电脑蓝屏怎么解决&#xff1f;遇到这个问题&#xff0c;可以从下面四个方法来入手&#xff0c;一键快速解决电脑蓝屏问题&#xff01; 操作环…

Android 深入系统完全讲解(9)

3 JNI 调试 开发 android 应用&#xff0c;主要围绕着 java 语言&#xff0c;可是&#xff0c;如果我们需要追求性能&#xff0c;或者需要调用之前我们已经写好的 c c so 库的时候&#xff0c;或者和硬件打交道的时候&#xff0c;那么我们就会接触到 JNI&#xff08;java nati…

Cadence:层次化电路+信号线束

之前用Altium Designer做工程&#xff0c;偏向于使用层次化电路信号线束的方式画图&#xff0c;类似下图&#xff1a; 现在工作需求使用Cadence&#xff0c;继续采用层次化电路设计信号线束的方式&#xff0c;总结如下&#xff1a; 打开OrCAD Capture&#xff0c;创建一个工程…