RabbitMQ不完整的笔记

同步的不足

1、拓展性差,当要添加功能时,需要在原来的功能代码上做修改,高耦合。
2、性能下降,调用者需要等待服务提供者执行完返回结果后,才能继续向下执行
3、级联失败,由于我们是基于OpenFeign调用交易服务、通知服务。当交易服务、通知服务出现故障时,整个事务都会回滚(回滚的范围取决于自己的设定)

而通过RabbitMQ就能解决上述问题,因为其是异步调用

安装 安装docker后,使用docker拉取RabbitMQ的镜像,进行部署

相关概念

publisher: 生产者,也就是发送消息的一方
consumer: 消费者,也就是消费消息的一方
queue: 队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理
exchange: 交换机,负责消息路由。生产者发送的消息由交换机决定投递到哪个队列
virtual host: 虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的exchange、queue
(与MySQL中的不同数据库相似)

SpringAMQP

RabbitMQ采用了AMQP协议,因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息,都可以与RabbitMQ交互,而Spring的官方刚好基于RabbitMQ提供了这样一套消息收发的模板工具:SpringAMQP
提供的三个功能:

自动声明队列、交换机及其绑定关系基于注解的监听器模式,异步接收消息封装了RabbitTemplate工具,用于发送消息

案例入门

采用的方案结合注解的方式没有使用控制台。方便快速在Spring项目中开发
publisher发送消息到交换机,交换机发送消息到队列,consumer发送接受队列中的消息

导入AMQP依赖

<!--AMQP依赖,包含RabbitMQ-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

WorkQueues模型

让多个消费者绑定到一个队列,共同消费队列中的消息
有什么好处?
消息的处理速度就能提高
最佳实践
**消息发送到队列,模拟大量消息堆积的队列

/*** workQueue* 向队列中不停发送消息,模拟消息堆积。*/
@Test
public void testWorkQueue() throws InterruptedException {// 队列名称String queueName = "simple.queue";// 消息String message = "hello, message_";for (int i = 0; i < 50; i++) {// 发送消息,每20毫秒发送一次,相当于每秒发送50条消息rabbitTemplate.convertAndSend(queueName, message + i);Thread.sleep(20);}
}

消息接受
两个消费者接收队列中的消息,接受者1每20ms接收一个,消费者2每200ms接受一个

@RabbitListener(queues = "work.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());Thread.sleep(20);
}@RabbitListener(queues = "work.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());Thread.sleep(200);
}

两个消息接收者都设置了Thead.sleep,模拟任务耗时:
消费者1 sleep了20毫秒,相当于每秒钟处理50个消息
消费者2 sleep了200毫秒,相当于每秒处理5个消息

可是实际测试结果是消费者1和消费者2竟然每人消费了25条消息:
消费者1很快完成了自己的25条消息,消费者2却在缓慢的处理自己的25条消息。
出现这种现象表明队列对于消息的分配并没有考虑到每个消费者的实际能力

优化配置
在yml文件中配置prefetch: 1 (prefetch: 1表示每个消费者每次只能从队列中预取1个消息,消费完就能拿下一次,不需要等轮询。它可以帮助保证每个消息在被消费者处理时都能得到较为均匀的分配,避免某个消费者处理速度慢而导致其他消费者空闲的情况。)

spring:rabbitmq:listener:simple:prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

交换机

作用: 在接受生产者消息的同时最重要的是如何处理消息,比如是交给所有队列还是交给某个特定的队列

Fanout: 将消息交给所有绑定到交换机的队列,就像上面的案例,默认是每个队列平均的接受消息

Direct: 基于RoutingKey(路由key)发送给订阅了消息的队列,交换机不再把消息交给每个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routing Key与消息的Routing Key完全一致,这个队列才能接收到消息

总结
Direct交换机与Fanout交换机的差异?

Fanout交换机将消息路由给每一个与之绑定的队列
Direct交换机根据RoutingKey判断路由给哪个队列
如果多个队列具有相同的RoutingKey,则与Fanout功能类似

Topic交换机
在Direct交换机中它对于队列接收消息的选择性是单一的,只能被单个的Routing Key绑定,而如果在将队列绑定Key时使用通配符绑定 就能将队列同时绑定多个Key
比如:

#:匹配一个或多个词
*:匹配不多不少恰好1个词
item.#:能够匹配item.spu.insert 或者 item.spu
item.*:只能匹配item.spu

接下来正式开始编码使用RabbitMQ,直接使用注解声明队列和交换机
消息发送者
后续前端请求该方法就可以发送消息

/*** topicExchange*/
@Test
public void testSendTopicExchange() {// 交换机名称String exchangeName = "hmall.topic";// 消息String message = "喜报!孙悟空大战哥斯拉,胜!";// 发送消息rabbitTemplate.convertAndSend(exchangeName, "blue", message);
}

消息接受者
声明Direct模式的交换机和队列

@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue1"),//。如果指定名称的队列不存在,则会自动创建该队列exchange = @Exchange(name = "hmall.direct", type = ExchangeTypes.DIRECT),//声明了direct的交换机key = {"red", "blue"}//声明了key
))
public void listenDirectQueue1(String msg){System.out.println("消费者1接收到direct.queue1的消息:【" + msg + "】");
}@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue2"),exchange = @Exchange(name = "hmall.direct", type = ExchangeTypes.DIRECT),key = {"red", "yellow"}
))
public void listenDirectQueue2(String msg){System.out.println("消费者2接收到direct.queue2的消息:【" + msg + "】");
}

结果就是只有listenDirectQueue1才能接收到消息

一、使用 RabbitMQ 作为中间通信层,实现不同编程语言间的通信,使用消息队列完成定时任务,保证功能可靠性

1.1、虚拟机安装RabbitMQ

首先是在虚拟机中安装配置RabbitMQ,基于docker安装,

docker pull rabbitmq

创建并运行 RabbitMQ 容器

docker run -d -p 15672:15672 -p 5672:5672 \-e RABBITMQ_DEFAULT_VHOST=my_vhost  \-e RABBITMQ_DEFAULT_USER=admin \-e RABBITMQ_DEFAULT_PASS=admin \--hostname myRabbit \--name rabbitmq \rabbitmq

1.2、SpringBoot部署RabbitMQ

添加依赖spring-boot-starter-amqp
修改yaml配置

spring:#rabbitmq 配置rabbitmq:host: 192.168.79.202username: guestpassword: guest#虚拟主机virtual-host: /#端口port: 5672listener:simple:#消费者最小数量concurrency: 10#消费者最大数量max-concurrency: 10#限制消费者,每次只能处理一条消息,处理完才能继续下一条消息prefetch: 1#启动时是否默认启动容器,默认为 trueauto-startup: true#被拒绝时重新进入队列的default-requeue-rejected: truetemplate:retry:#启用消息重试机制,默认为 falseenabled: true#初始重试间隔时间initial-interval: 1000ms#重试最大次数,默认为 3 次max-attempts: 3#重试最大时间间隔,默认 10000msmax-interval: 10000ms#重试的间隔乘数,#配置 2 的话,第一次等 1s,第二次等 2s,第三次等 4smultiplier: 1#在 RabbitMQ 中,initial-interval 和 max-interval 是用于指定消息重试机制的两个参数,#它们的区别如下:#1. initial-interval(初始间隔时间):表示第一次重试的时间间隔,也就是在消息第一次处#理失败后,等待多长时间再尝试重新发送消息。这个参数的默认值是 1 秒。#2.max-interval(最大间隔时间):表示重试过程中的最大时间间隔,也就是每次重试时,#最长等待多长时间再尝试重新发送消息。这个参数的默认值是 10 秒。

1.3、消息发送者

@Autowired
private AmqpTemplate rabbitTemplate;
//这里需要创建AmapTemplate对象,以便调用convertAndSend方法
@Test
public void testSendTopicExchange() {// 交换机名称String exchangeName = "hmall.topic";// 消息String message = "检测到摔倒";// 发送消息rabbitTemplate.convertAndSend(exchangeName, “ " message);
}

这里传入的参数跟使用什么交换机有关系,如果使用funout交换机 就队列交换机和消息,如果使用direct交换机 就需要传入key 交换机名字 消息

在这个例子中生产者使用默认的交换机 所以需要指定队列 而不用指定key(主要是完成语言之间的通信)

1.4、python部署RabbitMQ

python中使用pika操作RabbitMQ

# coding=utf-8
### 消费者import pikauser_info = pika.PlainCredentials('root', 'root')
connection = pika.BlockingConnection(pika.ConnectionParameters('ip', 5672, '/', user_info))
channel = connection.channel()# 如果指定的queue不存在,则会创建一个queue,如果已经存在 则不会做其他动作,生产者和消费者都做这一步的好处是
# 这样生产者和消费者就没有必要的先后启动顺序了
channel.queue_declare(queue='hello')# 回调函数
def callback(ch, method, properties, body):print('消费者收到:{}'.format(body))# channel: 包含channel的一切属性和方法
# method: 包含 consumer_tag, delivery_tag, exchange, redelivered, routing_key
# properties: basic_publish 通过 properties 传入的参数
# body: basic_publish发送的消息channel.basic_consume(queue='hello',  # 接收指定queue的消息auto_ack=True,  # 指定为True,表示消息接收到后自动给消息发送方回复确认,已收到消息on_message_callback=callback  # 设置收到消息的回调函数)print('Waiting for messages. To exit press CTRL+C')# 一直处于等待接收消息的状态,如果没收到消息就一直处于阻塞状态,收到消息就调用上面的回调函数
channel.start_consuming()

队列使用的是默认队列

1.5、整体流程

在前端界面点击 发送get请求 前端根据传过来的是1还是2 就来进行不同的活动

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MyController {@GetMapping("/example")public String exampleController(@RequestParam("param1") String param1, @RequestParam("param2") int param2) {// 处理参数并返回响应return ;}}

是1 就进行摔倒检测
后端使用RabbitMq的生产者的方法 发送消息到队列, python端接收到发送到指定队列的消息后开始调用摔倒检测 摔倒检测因为是使用的yolov8所以没有在本机运行,调用阿里云的服务
持续检测阿里云返回的是什么 如果返回的是摔倒了,就进行后续的活动
是2就通知python进行人脸检测安防

实现了python和java的通信之后再来说说如何实现的定时任务的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后在ServiceImpl填写具体的方法 使用RabbitMQ发送消息

 rabbitTemplate.convertAndSend(RabbitMQConfig.TTL_EXCHANGE, "ttl.test", orderInfo)

发送给ttl交换机 这个交换机绑定了ttl队列,同时ttl队列绑定了死信交换机,这个死信交换机绑定了死信队列

有由于这个ttl队列设置了过期时间,所以过期时间到后,消息就会到死信交换机

死信队列监听到之后就会开始处理,这样就能确保定时任务中不会确保定时没有成功的情况

RabbitMQ如何保证消息不丢失

生产者确认机制
RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程中丢失。消息发送到MQ以后,会返回一个结果给发送者,表示消息是否处理成功(避免了消息在发送交换机或者发送到队列丢失的情况)

MQ默认是内存存储消息,开启持久化功能可以确保缓存在MQ中的消息不丢失。
当内存出问题还有磁盘兜底

通过交换机持久化,队列持久化,消息持久化

消费者确认
RabbitMQ支持消费者确认机制,消费者处理消息后可以向MQ发送ack回执,MQ收到ack回执后才会删除该消息
最好采用auto的确认模式 即 自动ack 由spring检测istener代码是否出现异常,没有异常就返回ack,抛出异常就返回nack
如果消费者接受消息失败,也可以利用Spring的retry机制,在消费者出现异常时利用本地重试,设置重试次数,当次数达到以后,如果消息依然失败将消息投递到异常交换机,交由人工处理

RabbitMQ消息的重复消费问题

出现的原因是网络抖动等,消费者处理消息后因为网络问题没能成功发送确认给MQ,导致Spring的重试机制,就重复消费了消息
解决方案:
每条消息设置一个唯一的标识id
幂等方案 ,可以加锁

死信交换机(延迟队列)

延迟队列:进入队列的消息会被延迟消费的队列
场景:超时订单、限时优惠、定时发布

延迟队列=死信交换机+TTL
TTL,也就是Time-To-Live。如果一个队列中的消息TTL结束仍未消费,则会变为死信,ttl超时分为两种情况:
消息所在的队列设置了存活时间
消息本身设置了存活时间

在这里插入图片描述

延迟队列插件

可以使用DelayExchange插件
DelayExchange的本质还是官方的三种交换机,只是添加了延迟功能。因此使用时只需要声明一个交换机,交换机的类型可以是任意类型,然后设定delayed属性为true即可。

具体怎么使用
在这里插入图片描述

如果有100万消息堆积在MQ,如何解决

当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限。之后发送的消息就会成为死信,可能会被丢弃,这就是消息堆积问题

增加更多消费者,提高消费速度
在消费者内开启线程池加快消息处理速度
扩大队列容积,提高堆积上限

惰性队列的特征如下:
接收到消息后直接存入磁盘而非内存
消费者要消费消息时才会从磁盘中读取并加载到内存
支持数百万条的消息存储
配置的方式添加
在这里插入图片描述
注解的方式添加
在这里插入图片描述

RabbitMQ高可用机制

在生产环境下,使用集群来保证高可用性
普通集群、镜像集群、仲裁队列
暂时不看

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

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

相关文章

Visual Studio Code使用(C++项目新建,运行)

VS Code 直接在官网下载安装。 接下来安装插件&#xff0c;下图是C所需的对应插件 1.新建项目 VS Code下载安装完成后&#xff0c;直接进入欢迎页&#xff1a; 在访达/文件夹中新建一个文件夹&#xff0c;欢迎页点击【打开】&#xff0c;选择刚刚新建的文件夹。点击第一个图…

华为OD机试 - 最大坐标值(Java 2024 D卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

数据结构第三篇【链表的相关知识点一及在线OJ习题】

数据结构第三篇【链表的相关知识点一及在线OJ习题】 链表链表的实现链表OJ习题顺序表和链表的区别和联系 本文章主要讲解关于链表的相关知识&#xff0c;喜欢的可以三连喔 &#x1f600;&#x1f603;&#x1f604;&#x1f604;&#x1f60a;&#x1f60a;&#x1f643;&#…

Makefile的入门学习

一、Makefile的入门学习 编译工具及构建工具介绍 在之前的课程&#xff0c;都是直接使用gcc对代码进行编译&#xff0c;这对简单的工程是可以的&#xff0c;但当我们遇到复杂的工程时&#xff0c;每次用gcc等编译工具去操作就会显得很低效。因此make工具就出现了&#xff0c;…

LeetCode1137第N个泰波那契数

题目描述 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。 解析 递归应该会超时&#xff0c;可以用循环&#xff0c;或者官方解法的矩阵的幂。 public int tr…

(4) qml动态元素

文章目录 概述注意 动画元素变化的策略Animation on 变化behavior on⽤standalone animation注意 缓冲曲线&#xff08;Easing Curves&#xff09;动画分组 概述 这⼀章介绍如何控制属性值的变化&#xff0c;通过动画的⽅式在⼀段时间内来改变属性值。这项技术是建⽴⼀个现代化…

获取(复制)网页上的文字

获取&#xff08;复制&#xff09;网页上的文字 今天在搜索历史课本上一段文言文的翻译时&#xff0c;找到的网页&#xff0c;屏蔽了右键&#xff0c;不能选择&#xff0c;当然不让复制啦。对于这样的网站可以采用如下方法进行数据的获取&#xff0c;以chrome为例。 1、网页另…

keil5常见使用技巧记录(更新)

快速到函数定义 F12或自己定义快捷键CTRLK&#xff08;个人设定&#xff09; 修改快捷键 下图实例是快速跳转到函数或变量定义位置&#xff0c;当然可以定义其他功能快捷键&#xff0c;如快速注释多行&#xff0c;快速消除注释等 标记全部查找变量的蓝色框取消 CTRLshiftF2…

【YOLOv10改进[Backbone]】图像修复网络AirNet助力YOLOv10目标检测效果 + 含全部代码和详细修改方式 + 手撕结构图 + 全网首发

本文带来的是图像复原网络AirNet&#xff0c;它由基于对比度的退化编码器( CBDE )和退化引导的恢复网络( DGRN )两个模块组成。可以在一个网络中恢复各种退化图像。AirNet不受损坏类型和级别的先验限制&#xff0c;仅使用观察到的损坏图像进行推理。本文中将使用图像修复网络Ai…

使用Python绘制瀑布图

使用Python绘制瀑布图 瀑布图效果代码 瀑布图 瀑布图&#xff08;Waterfall Chart&#xff09;是一种数据可视化工具&#xff0c;用于展示累积数值的变化&#xff0c;尤其适合于展示随时间或过程中的增减变化。它通常用于财务分析&#xff0c;如展示收入、支出和净利润的变化过…

【离散数学】数理逻辑集合论知识点汇总

期末题型&#xff1a; 一、 单选题&#xff08;每题2分&#xff0c;10题共20分&#xff09; 命题判定、哈斯图边计算等 二、 填空题&#xff08;每空1分&#xff0c;共20分&#xff09; 与非和或非的表示等 三、 简答题&#xff08;10题&#xff0c;每题6分&#xff0c;共60分&…

安装禅道,帮助测试,测试打磨项目精度。

先检查docker版本。 sudo docker network create --subnet172.172.172.0/24 zentaonet sudo docker run --name zentao2 -p 8080:80 -p 3307:3306 --networkzentaonet --ip 172.172.172.3 -e MYSQL_INTERNALtrue -v /media/cykj/3T/ze…

【十年java搬砖路】Jumpserver docker版安装及配置Ldap登陆认证

Jumpserver docker 安装启动教程 拉取镜像 docker pull JumpServer启动进行前确保有Redis 和Mysql 创建jumperServer数据库 在MYSQL上执行 创建数据库 登陆MYSQL mysql -u root -p 创建Jumperserveri库 create database jumpserver default charset utf8mb4;可以为jumperSe…

【软件开发】Java学习路线

本路径视频教程均来自尚硅谷B站视频&#xff0c;Java学习课程我已经收藏在一个文件夹下&#xff0c;B站文件夹同时会收藏其他Java视频&#xff0c;感谢关注。指路&#xff1a;https://www.bilibili.com/medialist/detail/ml3113981545 2024Java学习路线&#xff08;快速版&…

揭秘YAML:Python中的PyYAML应用

&#x1f340; 前言 博客地址&#xff1a; CSDN&#xff1a;https://blog.csdn.net/powerbiubiu &#x1f44b; 简介 本章节介绍YAML文件格式的操作&#xff0c;通过Python的第三方库 PyYaml 来实现文件操作&#xff0c;在 Pyhon 代码中无论读取还是写入&#xff0c;都是使用…

【机器学习】逻辑回归:原理、应用与实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 逻辑回归&#xff1a;原理、应用与实践引言1. 逻辑回归基础1.1 基本概念1.2 Sig…

视频汇聚EasyCVR安防系统对接公安部GA/T 1400视图库布控、告警、订阅流程描述

随着信息技术的飞速发展&#xff0c;视频监控在公共安全领域的应用越来越广泛&#xff0c;对于视频监控系统的要求也日益严格。为了满足公安系统对视频图像信息应用的高标准需求&#xff0c;视频汇聚平台EasyCVR视频监控系统全面支持GA/T 1400标准协议&#xff0c;为公安部门提…

基于JSP的母婴用品网站系统

你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有需求可以文末加我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 管理员功能界面 用户功能界面 前台首页功能界面 …

Java开发-面试题-0003-List、Set 和 Map的区别

Java开发-面试题-0003-List、Set 和 Map的区别 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&#xff09; 生活公众…

BioPorto胰高血糖素样肽-1抗体(GLP-1)

丹麦BioPorto Diadnostics公司致力于提供世界领先的GLP-1抗体。基于结合GLP-1位点的不同&#xff0c;他们筛选出了不同的抗GLP-1抗体。有的抗体可以同时结合GLP-1的活性形式和非活性形式&#xff0c;有的专门结合生物活性形式的GLP-1。在开发和检测GLP-1相关治疗的过程中&#…