乐趣区

关于rabbitmq:RabbitMQ-解决消息延迟和堆积问题

大家好,我是小菜。
一个心愿可能成为 吹着牛 X 谈架构 的男人!如果你也想成为我想成为的人,不然点个关注做个伴,让小菜不再孤独!

本文次要介绍 RabbitMQ 的常见问题

如有须要,能够参考

如有帮忙,不忘 点赞

微信公众号已开启,小菜良记,没关注的同学们记得关注哦!

  • 音讯可靠性问题:如何确保发送的音讯至多被生产一次?
  • 提早音讯问题:如何实现音讯的提早投递?
  • 音讯沉积问题:如何解决数百万级以上音讯沉积,无奈及时生产问题?

咱们在上篇曾经阐明了如何解决 音讯失落 的问题,也就是保障了音讯的可靠性,那么其余两个问题同样重要,这篇咱们将讲述其余两个问题的解决形式~!

音讯失落解决方案:《RabbitMQ》| 音讯失落也就这么回事

一、提早音讯

提早音讯 字面意思就是让提早接管音讯,那么如何能让音讯提早达到?这就是咱们要思考解决的问题,在理解提早队列之前咱们须要先明确 RabbitMQ 中的两个概念

  • 死信交换机
  • TTL

1)死信交换机

死信(dead letter),也就是废除已死亡的音讯,那什么状况下一个一般的音讯可能成为 死信?须要合乎以下三个条件:

  1. 消费者应用 basic.rejectbasic.nack 申明生产失败,并将音讯的 requeue 参数设置为 false
  2. 音讯是一个过期音讯,超时后无人生产
  3. 要投递的队列音讯沉积满了,最早的音讯就会成为死信

死信交换机 便是 死信 的归属。

如果一个队列配置了 dead-letter-exchange 属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机就称为 死信交换机 - DLXDead Letter Exchange

步骤:当生产者失常投递到队列(simple.queue)中,如果消费者从队列(simple.queue)生产音讯却申明了 reject,那并且队列绑定了死信交换机(dl.queue),那么这个时候成为死信的音讯就会投递到这个死信队列(dl.queue)中。

失常队列 --> 死信队列 的过程,咱们必须申明两个要害信息

  • 死信交换机的名称
  • 死信交换机与死信队列绑定的路由 key

而这两个信息也是咱们投递音讯的根底配置。

接下来咱们简略模仿一下 条件 1 所产生的场景

1、首先申明一个死信交换机和死信队列

咱们这边是应用简略的注解形式间接生成

通过 RabbitMQ 控制台界面能够看出曾经胜利生成

2、申明失常应用交换机与队列

而后这个时候咱们就能够创立一个失常应用的交换机与队列,并指明死信交换机

同样能够通过控制台查看创立状态

其中是否有申明死信交换机咱们能够通过队列的 DLXDLK 标记判断

3、模仿拒收

而后咱们当初通过代码模仿客户端回绝音讯的场景

1)音讯发送

2)音讯接管

查看控制台,后果如下:

2021-11-06 23:56:52.095  INFO 2112 --- [ntContainer#0-1] c.l.m.c.listener.SpringRabbitListener    : 失常业务交换机 | 接管到的音讯 : [hello]
2021-11-06 23:56:52.118  INFO 2112 --- [ntContainer#1-1] c.l.m.c.listener.SpringRabbitListener    : 死信交换机 | 接管到的音讯 : hello

这阐明咱们死信交换机曾经胜利发挥作用

2)TTL

以上咱们曾经胜利意识到了 死信交换机 的应用,然而这与咱们一开始说的 提早队列 仿佛并没有太大关系,莫急~ 接下来说到的 TTL(Time-To-Live) 就是用来解决提早音讯的~!

在 TTL 的概念中,如果一个队列中的音讯 TTL 完结后仍未被生产,那么这个音讯就会主动变为死信,而 TTL 超时状况分为两种:

  1. 音讯所在的队列设置了存活工夫
  2. 音讯自身设置了存活工夫

咱们同样进行上述 条件 2 的模仿场景

1、申明死信交换机与死信队列(上述已实现)
2、申明提早队列并指定死信交换机

同样控制台查看创立后果,并且咱们发现不止有 DLXDLK 标记,还多了个 TTL,阐明该队列是提早队列

3、模仿生产超时状况

咱们往提早队列中发送一条音讯,并且没有消费者进行生产,期待 1 分钟后查看是否能进入 死信队列

咱们曾经发送了一条音讯到提早队列并且一分钟后也胜利在控制台发现了这条信息曾经进入到了死信交换机

2021-11-07 00:01:30.854  INFO 32752 --- [ntContainer#1-1] c.l.m.c.listener.SpringRabbitListener    : 死信交换机 | 接管到的音讯 : test ttl-message

以上是配置了 队列超时 工夫,音讯自身天然也能配置超时工夫,当 音讯 队列 都存在超时工夫时,那么就以最短的 TTL 为准,音讯的超时配置如下:

如上图所示,咱们能够利用 Message 这个类来传递音讯信息,并设置上超时工夫,咱们设置的是 5000 ms,期待发送胜利后,控制台过 5000 ms 也胜利打印了死信交换机生产的音讯:

2021-11-07 00:03:09.048  INFO 39996 --- [ntContainer#1-1] c.l.m.c.listener.SpringRabbitListener    : 死信交换机 | 接管到的音讯 : this is a ttl message

3)提早队列

咱们上述是应用 死信交换机 来间接实现 提早队列 的成果,但理论在 RabbitMQ 不用如此麻烦,RabbitMQ 曾经为咱们封装好了插件,咱们只须要下载安装即可~

RabbitMQ 插件下载地址

咱们进入地址能够发现有许多插件,搜寻 delay 关键字找到咱们须要的插件进行下载

下载完后间接上传到 RabbitMQ 的插件目录 – plugins,小菜这边是应用 docker 长期装置测试的,所以曾经将该插件目录挂载进去了:

docker run -itd --name rabbitmq -v plugins:/plugins -p 15672:15672 -p 5672:5672 rabbitmq:management

因而我这边间接将插件上传到容器中的 plugins 目录即可~

而后进入到容器中执行以下命令进行插件开启

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

并且咱们在控制台创立交换机的时候能够看到 type 类型多了个选项

胜利执行到这步就阐明曾经开启了 RabbitMQ 的提早队列性能

那接下来咱们就能够来应用 DelayExchange,首先咱们须要理解代码的形式创立提早交换机:

形式 1

形式 2

当咱们万事具备之后就能够来发送音讯了

在发送音讯的时候,音讯头中肯定要携带上 x-delay 参数,指定上延迟时间

通过这样配置之后,咱们能够在控制台看到,通过 10 秒后 delay.queue 才收到对应音讯,而后被对应消费者生产

3)总结

咱们下面从 死信交换机 TTL 提早队列,一步步意识了如何实现提早音讯的性能,而后咱们进行一个小小的总结:

问题 1:什么样的音讯会成为死信?
  1. 音讯被消费者 reject 或返回 nack
  2. 音讯超时未及时生产
  3. 音讯队列满了
问题 2:音讯超时的形式
  1. 给队列设置 TTL 属性
  2. 给音讯设置 TTL 属性
问题 3:如何应用提早队列
  1. 下载并启用 RabbitMQ 提早队列插件
  2. 申明一个交换机,并将 delayed 属性设置为 true
  3. 发送音讯时,增加 x-delay 头,值为超时工夫
问题 4:提早队列的应用场景
  1. 提早发送短信告诉
  2. 订单主动勾销
  3. 库存主动回滚

二、惰性队列

讲完提早队列,咱们持续来意识 惰性队列

惰性队列 之前,咱们先抛出一个问题~

RabbitMQ 如何解决音讯沉积问题

什么状况下会呈现音讯沉积问题?

  1. 当生产者生产速度远远消费者生产速度
  2. 当消费者宕机没有及时重启

那么如何解决这个问题?通常思路如下:

  1. 在消费者机器重启后,减少更多的消费者进行解决
  2. 在消费者解决逻辑外部开拓线程池,利用多线程的形式进步处理速度
  3. 扩充队列的容量,进步沉积下限

这几个形式从实践上来说解决音讯沉积问题也是没有问题的,然而解决形式不够优雅甚至不够灵便~ 那么除了以上的几种解决形式,咱们能够利用 RabbitMQ 中自带的一种队列类型 — 惰性队列

什么是惰性队列?咱们认识一下惰性队列的几个个性:

  • 接管到音讯后间接存入磁盘而非内存
  • 消费者要生产音讯时才会从磁盘中读取并加载到内存中
  • 它反对百万级音讯的存储

说到底,就是利用磁盘的缓冲机制,而这种机制的毛病就是 音讯的时效性会升高,性能受限于磁盘的 IO,意识个性和毛病之后,咱们便来看看如何创立惰性队列

形式 1

形式 2

形式 3

该形式是间接基于命令行批改将一个正在运行中的队列批改为惰性队列

rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"lazy"}' --apply-to queues  

其中几个命令参数含意如下:

  • rabbitmqctl:命令行工具
  • set_policy:增加一个策略
  • Lazy:策略名称,能够自定义
  • ^lazy-queue$:用正则表达式匹配队列的名称
  • ‘{“queue-mode”:”lazy”}’:设置队列为 lazy 模式
  • –apply-to queues:策略的作用对象,是所有的队列

这种惰性队列的形式只管毛病是音讯时效性会升高,然而在某些场景下也不是不能承受,何况它的长处同样显著:

  • 基于磁盘存储,音讯下限高
  • 没有间歇性的 page-out,性能稳固

到这里,咱们就曾经讲述了 RabbitMQ 的常见问题,对于咱们来说,一般的开发场景可能比拟少遇到这些问题,然而没遇到不等于没有,所以咱们还是须要多意识来防患于未然!

不要空谈,不要贪懒,和小菜一起做个 吹着牛 X 做架构 的程序猿吧~ 点个关注做个伴,让小菜不再孤独。咱们下文见!

明天的你多致力一点,今天的你就能少说一句求人的话!
我是小菜,一个和你一起变强的男人。 💋
微信公众号已开启,小菜良记,没关注的同学们记得关注哦!

退出移动版