硬卷消息中间件系列(九):RabbitMQ 队列与消息过期

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

ee7c981218f64dfc3e96f947cff92b7b.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

来源:blog.csdn.net/yi_qingjun
/article/details/128435895

334bc65353211528722fc7f3fb3230e4.jpeg


一、过期时间

  • 这里过一个知识点——过期时间,即对消息或队列设置过期时间(TTL)。一旦消息过期,消费就无法接收到这条消息,这种情况是绝不允许存在的,所以官方就出了一个对策——死信队列,死信队列最初出现的意义就是为了应对消息过期丢失情况的手段之一。

  • 那么过期时间具体怎么设置呢?运维人员只需了解外部层面的,因为对消息设置过期时间有两个维度,一个是定义消息本身属性时开发人员在代码里加进去的一个参数(初中级运维看不到),另外一个是定义队列属性的,可以从web监控页面查看。

  • 什么是过期时间?

    • 指对消息和队列设置 过期时间,简称TTL,是Time to Live 的简称。

  • TTL设置对象和方式?

    • 第一个是,通过队列属性设置,队列中所有消息都有相同的过期时间。在web监控中的“ x-message-ttl ”参数。

    • 第二个是,对消息本身进行单独设置,每条消息的 TTL 可以不同。此种方式只能通过代码来设置,在web监控中发送消息已不支持该种消息参数。

    • 如果两种维度一起使用,则消息的 TTL 以两者之间较小的那个数值为准。

    • 针对队列设置过期时间,从队列创建开始算起,一旦达到设置的过期时间后,队列自动删除,队列里的所有消息都会被删除。在web监控中的“ x-expires ”参数,也是上一章节中咱们提到过的。

    • 针对消息设置过期时间,有两个维度:

1.1 针对队列设置

1.创建ceshi_1队列时,使用“x-expires”参数指定过期时间,单位毫秒。我这里设置的是5s后自动删除。
59d474e95ae2b00cc0fb0f0bf0c5c3b4.png2. 创建后,会显示Exp,表示已设置过期时间。
ceea7aec49bc2d3a8b89bff4a8fd3137.png3. 5s后再次查看,ceshi_1队列已自动删除。

4a639b8e007b6b27e8e84206d3b00698.png
1.2 针对消息设置
  • 这里演示第一种维度设置,通过对队列参数设置来控制消息过期时间。此种方式设置后,所有进入该队列的消息只能存活自定义的那个时间。

  1. 创建ceshi_2队列时,通过指定“x-message-ttl”参数设置该队列里的消息过期时间,单位毫秒。我这里设置的是5s,代表进入ceshi_2队列的所有信息都只会保留5s,之后自动删除。

7fa96fd616eb42d935c10c69f0df0e6c.png
2.ceshi_2队列创建完后同样也有个标志TTL,代表已对该队列的消息设置了过期时间。

f9930503eb4770e1d842aa825af141fd.png
3. 现在对ceshi_2队列发布消息“wuhan”。
95b8730c51b8a72a921cfdfa57533401.png
0fa137d31f4de5fee8b87aa7f0beca2c.png

  1. 5s时间到,消息自动删除,队列显示挤压队列为0。

ec27073d07d0292b8aa7a2dae69c2f91.png

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

二、死信队列

  • 咱们前面铺垫讲了一下消息的过期时间,当消息过期后消费者收不到消息,这对于公司业务来说非常严峻,所以当消息过期后,它在队列中会变成死信队列。

  • 消息变成死信的几种情况:

    • 消息被拒绝 (Basic.Reject/Basic.Nack),并且设置 requeue 参数为 false。

    • 消息过期。

    • 队列达到最大长度,即将删除一些。

2.1 死信交换器
  • 死信交换器是什么?

    • 死信交换器,英文名为Dead-Letter-Exchange,简称DLX,也有人称之为死信邮箱。当消息在一个队列中变成死信 (dead message) 之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX,绑定 DLX 的队列就称之为死信队列。

    • DLX 也是一个正常的交换器,和一般的交换器没有区别,只是在创建某个队列时指定某个交换器为死信交换器,是队列的属性配置。当这个队列中存在死信时,该死信会重新发布到设置的 DLX 上去,进而被路由到死信队列。

  1. 现在有两个正常交换器,都开启了持久化。其中qingjun_exchange交换器还没有绑定队列,baimu_exchange交换器通过baimu-baimu_key绑定键与队列baimu_queue绑定。

6602924d20e170b499fc94bcfd678a59.png
b14ca2f1871728e477c7202290588a53.png

2.创建队列qingjun_queue时,通过参数“x-dead-letter-exchange”指定死信交换器,也可以通过“x-dead-letter-routing-key”指定绑定键,如果没设置绑定键,则使用死信交换器原来的绑定键。比如我这里没有指定死信交换器绑定键就还是使用原来的绑定键“baimu-baimu_key”。

d8721b576342344d9758a0dfb2fb85fd.png
34f35bf784b06f3a2c9be59c3438a4a4.png3. 将队列qingjun_queue与交换器qingjun_exchange绑定,这样一来,当qingjun_queue里存在死信时,会把死信发给baimu_exchange,再到死信队列baimu_queue里。

032be1ea5d8fac4c44eb84491d4e3aab.png
2.2 死信队列原理
  • 如下图,baimu_exchange为死信交换器,baimu_queue为死信队列。

c1b306ce36d3dc73bad07d4541a3f8fd.png
  1. 发一条“wuhan”消息给qingjun_queue队列。

fd5feeb45ed5f473083924022ff97607.png2. 此时消息在qingjun_queue队列里。
001779e329fb241323d7a97df60c69e5.png3. 等到了过期时间后,消息从qingjun_queue队列里消失,并存在了死信baimu_queue队列里。
5c896adec71bdef38f817f8ea0ba4e3e.png
24117ec78eb48f1f47465ba8de4614ae.png

2.3 延迟队列(特殊用法)
  • 延迟队列并非是rabbitmq的直接功能,而是通过死信队列和过期时间配合使用演练模拟延迟队列的一种用法。

  • 什么是延迟队列?

    • 延迟队列存储的对象是对应的延迟消息,所谓“延迟消息”是指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。

    • 回看死信队列的工作流程,当我想拿死信队列里的消息时,这种情况下,这个死信队列也是延迟队列。为什么我会想拿死信队列里的消息呢?有以下场景可以适用。

  • 适用场景

    • 在订单系统中,一个用户下单之后通常有 30 分钟的时间进行支付,如果 30 分钟之内没有支付成功,那么这个订单将进行异常处理,这时就可以使用延迟队列来处理这些订单了。

    • 当我下班后,还在公司们口就可以通过手机远程遥控家里的空调在我上地铁后就开始制冷工作,这时就可以把我的请求指令消息放发送到设有过期时间的队列中,过期时间就是我从公司走到地铁的时间,此时消息过期发送到死信队列里,再推送到消费者空调开始制冷。这里的死信队列就可以看作延迟队列。

3c9f4f68dcfb29dce4bd19754b92a494.png

现在我有以下需求,刚下班,想一回到家就能吹到25°C制冷、自动风速模式的空调。从公司走到地铁站需要20分钟,在我达到地铁站时家里的空调开始以16°C、5级风速模式制冷,该模式下制冷时间为我地铁通勤时间10分钟,等我下地铁时再调成25°C、2级风速模式制冷,下地铁到家需要10分钟,这10分种内空调需要调成25°C、自动风速模式工作,等我回到家正好是25°C、自动风速。

  • 在公司门口就远程控制家里空调,设置三个定时工作:

    • 第一个定时是在20分钟后开始工作,这一指令是第一条消息,完全匹配到绑定键_1到达queue_1,20分钟后消息过期,到达延迟队列_1,消费者开始消费第一条消息。

    • 第二个定时是在30分钟后开始工作,这一指令是第二条消息,完全匹配到绑定键_2到达queue_2,30分钟后消息过期,到达延迟队列_2,消费者开始消费第二条消息。

    • 第三个定时就是40分钟后开始工作,这一指令是第三条消息,完全匹配到绑定键_3到达queue_3,40分钟后消息过期,到达延迟队列_3,消费者开始消费第三条消息。

  • 20分钟后,我走到地铁站,开始消费第一条消息,空调开始以16°C、5级风速制冷。

  • 地铁通勤时间10分钟,此时开始消费第二条消息,空调开始以25°C、2级风速制冷。

  • 10分钟后下地铁,第三条消息开始消费,空调调成25°C、自动风速模式。

  • 等我回到家时,空调温度正好是25°C、自动风速。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

三、优先级队列

  • 通过参数 x-max-priority可以设置优先级队列和优先级消息。优先级高的队列具有高的优先权,优先级高的消息具备优先被消费的特权。

  • 适用场景

    • 业务数据大,大到经常挤压,但又要满足业务需求。

    • 优先级队列的使用需要看业务情况的,如果消费者的消费速度大于生产者的速度且 Broker 中没有消息堆积情况下,对发送的消息设置优先级就没有什么实际意义了。因为生产者刚发送完一条消息就被消费者消费了,那么就相当于 Broker 中至多只有一条消息,对于单条消息来说优先级是没有什么意义的。

3.1 监控页面创建优先级队列
  1. 创建wuhan_queue队列时添加参数x-max-priorit,就创建了优先级队列,并显示有“Pri”标识。

a524c74eb62830a24e1e9d87cefe12d5.png
3.2 监控页面创建优先级消息
  • 消息的优先级最大不能超过队列的最大优先级,默认最低为0。

  • 优先级高的先被消费,优先级低的后被消费。

  1. 在web监控里创建消息时,可以指定消息相关参数,当前版本有效参数如下显示,其中就有个优先级的参数priority。

20a35f12ef09a0ac65edba027f4b9b3b.png2. 创建第一条消息"beijing",优先级为10,创建第二条消息“wuhan”,优先级为4。beijing会先被消费,wuhan后被消费。

595bd41e203ccaf028b5adccc95dda01.png

四、回调队列

  • 回调队列,指在RPC调用过程中需要使用的回复的队列。

4.1 RPC的定义
  • RPC,是 Remote Procedure Call 的简称,即远程过程调用。

  • 它是一种通过网络从远程计算机上请求服务,而不需要了解底层网络的技术。RPC 的主要功用是让构建分布式计算更容易在提供强大的远程调用能力时不损失本地调用的语义简洁性。

    • 打个比方,现有两台服务器A和B,一个应用部署在 A 服务器上,想要调用B服务器上应用提供的函数或方法,由于不在同一个服务器上不能直接调用,所以需要需要通过网络来表达调用的语义和传达调用的数据。

  • RPC 的协议有很多,比如CORBA、Java RMI、WebService 的 RPC 风格、Hessian、Thrift 、Restful API。

4.2 PRC工作机制

RPC工作流程

  1. RPC客户端在生产者服务器上,RPC服务端在消费者服务器上。

  2. 生产者向一个普通队列baimu_queue发送一条消息,消息内容为调用消费者服务器上的函数A,并通过reply_to参数指定消费者接收到生产者的信息并处理完后发送到哪个队列上,此队列就称之为回调队列,通过correlation_id参数设置消息唯一表示符,好让生产者识别是哪一个请求回复的消息。

  • 消息是通过底层的网络协议传递到消费者服务器上,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给消费者服务器。

消费者收到消息时,进行调用函数A业务处理,业务处理完成后,将处理结果和收到消息的唯一标识符打包发送到回调队列qingjun_queue,这条消息可以称为响应消息。

  • 消费者服务器收到消息后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。将返回值再以序列化方式放给生产者服务器。

生产者服务器接到信息后,再反序列化,恢复为内存中的表达方式,此时才会在回调队列中显示回复信息,再然后再并根据消息唯一标识符进行处理。

71944fdc70661e572707ddc81fac6f91.png
4.3 监控页面上使用
  1. 在生产者服务器上发送消息时,指定correlation_id和reply_to参数。
    80351bb86ecec86c948fe19beffffe7f.png

  2. reply_to参数指定的回到队列需要先创建好,可以是一个普通队列,只是用来接收回调消息的。这里就不多此一举了。

五、惰性队列

  • 默认情况下,当生产者将消息发送到rabbitmq时,队列中的消息会尽可能地存储在内存之中,这样可以更加快速地将消息发送给消费者。即使是持久化的消息,在被写入磁盘的同时也会在内存中驻留一份备份。当消费者由于各种各样的原因,比如消费者下线、宕机、由于维护而关闭等原因致使长时间内不能消费消息而造成堆积时,使用惰性队列就可以很好解决了。

  • 惰性队列作用

    • 惰性队列会将接收到的消息直接存入磁盘中,而在消费者消费到相应的消息时才会被加载到内存中。

    • 它的出现就是可以有效解决消费者异常时可以存储大量挤压得消息,所以惰性队列能支持更多的消息存储。

  • 惰性队列对服务器资源要求:

    • 会增加磁盘I/O读写能力。惰性队列是将消息直接存盘,不管是持久化的或者是非持久化的,这样可以减少了内存的消耗,但是会增加 I/O 的使用,如果消息是持久化的,那么这样的 I/O操作不可避免。

    • 注意如果惰性队列中存储的是非持久化的消息,内存的使用率会一直很稳定,但是重启之后消息一样会丢失。

  • 队列模式

    • 分Default 持久化模式和 Transient瞬时模式,前者队列持久化,可以保证数据的高可靠;后者数据只在内存里,服务器关闭数据消失。

    • 在高版本中,比如我这里的3.11.5版本,可以在创建队列指定“x-queue-mode”参数设置该队列为惰性队列。

5.1 监控页面上使用
  • 如下图,在创建队列test_queue时,添加参数“x-queue-mode”会默认带出“lazy”一词,代表设置为惰性队列。

ee1826341a23e002d7e32aedc9620682.png
  • 惰性队列也同样会显示一个标识“Args”。

05df99212aa18f00a088033e0b6d6583.png

欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

2cae85fd4c2d939a0af019cfcbe75a3c.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

f5b7537c7ff1395368c6f32e4a96e603.png

52edd53f44415dbe36291ccc78a95ed4.png4ee7b1d3d0411217f86b8b09d68f335c.png70ae03851f23c84b1e7f26816b1449fd.pngf33d830fba1272231bbe86a7c4f27132.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值