RabbitMQ高级特性TTL,死信队列,延迟队列,当出现发送文件给docker内部,但是又无法使用拖拽模式的时候的办法,Enabling plugins on node rabbit

目录

TTL

延迟队列

出现发送文件给docker内部,但是又无法使用拖拽模式的时候的办法(以及出现Enabling plugins on node rabbit@91e7e7949d70:rabbitmq_delayed_message_exchangeError:{:plugins_not_found, [:rabbitmq_delayed_message_exchange]}报错的解决方法

问题二:UI 界面不显示消息条数

问题三:Listener method could not be invoked with the incoming message和 Cannot convert from [java.lang.Strin 

介绍一下延时队列

总结 


TTL

过期时间,消息的寿命,存活时间,当消息到达设置的过期时间还没有被消费掉,就会被清除掉

设置队列的TTL

1.设置队列的TTL(存在该队列中的所有消息的TTL)

2.设置消息的TTL

两个min,假如队列TTL是20s,消息TTL是10s,那么消息的TTL取小值,也就是10s

package com.example.controller;

import com.example.constant.Constants;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RequestMapping("/producer")
@RestController
public class ProducterController {
    @Resource(name="rabbitTemplate")
    RabbitTemplate rabbitTemplate;
    @Resource(name="confirmrabbitTemplate")
    //会自己查找
    private RabbitTemplate confirmrabbitTemplate;
    @RequestMapping("/ack")
    public String ack(){
        rabbitTemplate.convertAndSend(Constants.ACK_EXCHANGE,"ack","consumer ack mode test ...");
        return "消息发送成功";
    }
    @RequestMapping("/pres")
    public String pres(){
        Message message=new Message("Presistent test ...".getBytes(),new MessageProperties());
        //非持久化
        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
        rabbitTemplate.convertAndSend(Constants.PRES_EXCHANGE,"pres",message);
        return "消息发送成功";
    }
    @RequestMapping("/confirm")
    public String confirm(){
        //设置回调方法
        CorrelationData correlationData=new CorrelationData("1");
       confirmrabbitTemplate.convertAndSend(Constants.CONFIRM_EXCHANGE,"confirm,","confirm test...",correlationData);
        return  "消息发送成功";
    }
    @RequestMapping("/return")
    public String returns(){
        //设置回调方法
        CorrelationData correlationData=new CorrelationData("5");
        confirmrabbitTemplate.convertAndSend(Constants.CONFIRM_EXCHANGE,"confirm,","returns test...",correlationData);
        return  "消息发送成功";
    }
    @RequestMapping("/retry")
   public String retry(){
        rabbitTemplate.convertAndSend(Constants.RETRY_EXCHANGE,"retry","retry test ...");
        return "消息发送成功";
    }
    @RequestMapping("/ttl")
    public String ttl(){
        System.out.println("ttl...");
        MessagePostProcessor messagePostProcessor=new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {               //设置消息的TTL
                message.getMessageProperties().setExpiration("10000");  //毫秒,过期时间为10s
                return message;
            }
        };
        rabbitTemplate.convertAndSend(Constants.TTL_EXHCHANGE,"ttl","ttl test......",messagePostProcessor);
        return "消息发送成功";
    }

}

我们设置消息的过期时间为10s,可以观察到10s后消息自动消失


如果同时设置消息的ttl和队列的ttl,看看以哪个为主。

ttl未设置过期时间

ttl2设置过期时间20s        ->共同发送一条ttl消息等于10s的消息

两者区别

设置队列TTL的属性方法,一旦消息过期,就会从队列中删除,

设置消息TTL的方法,即使消息过期,也不会马上从队列中删除,而是在即将投递到消费者之前进行判定

为啥两种方法不一样呢

因为设置队列过期时间,队列已经过期的消息在队列头部,RabbitMQ只需要定期检查队头,看是否有过期的元素即可

而设置消息TTL的方式,每条消息的过期时间不同,如果要删除所有过期消息,需要扫描整个队列因此,不如等到消息即将被消费时候,再去判定是否过期,如果过期再进行删除即可(假如消息1 30s过期,消息2 10s过期,他会先判断消息1,然后消息1的30s过去了,再看消息2.

死信:由于种种原因,无法被消费的消息就是死信,

有了死信,就有死信队列:当消息在一个队列中变成死信之后,他能重新被发送到另一个交换机中,这个就是死信交换机,绑定死信交换机的就叫死信队列。

消息变成死信的情况:

1.消息被拒绝,并且requeue(重新入队)false;

2.消息过期

3.队列达到最大长度

标签对应

原本是10,但是当前是none,原因是原先有队列,但是你改变队列的长度,所以无法改变.

解决方法:删除原先队列,重新声明

此时,这里面的lim就是最大长度

当队列到达最大长度时,其余会到达死信

死信队列的面试题

死信队列的概念:首先介绍什么是死信:消息队列中一种特殊消息,指无法被正常消费或者处理的消息

死信的来源:消息在队列中存活的时间超过了设定的TTL

消息被拒绝:消费者在处理消息时候,肯能因为消息内容有误,处理逻辑异常等原因拒绝处理该消息,如果拒绝时,指定不重新入队,消息也会成为死信

(NACK重新入队时候,出现问题,假如他一直入队,会导致后面一直停滞,生产者一直生产消息,导致消息积压,所以需要把他放入死信队列中,

队列满了,队列到达最大长度,无法容纳新的消息时候,新来的消息会变成死信

死信队列的应用场景:

如:用户支付订单后,支付系统会给订单系统返回当前订单的支付状态,为了保证支付信息不丢失,需要使用到死信队列机制,当消息消费异常时候,将消息投入到死信队列中,由订单系统的其他消费者来监听这个队列,并对数据进行处理(比如发送工单,进行人工确认等)

延迟队列

消息被发送之后,并不希望消费者立即拿到消息,而是等待特定时间后,消费者才能拿到这个信息进行消费(相当于写给十年后的自己?)

智能家居,指定时间工作

RabbitMQ本身没有直接支持延迟队列的功能,但是可以通过前面所介绍的TTL+死信队列的方式组合,模拟出延迟队列的功能。

比如消费者消费死信队列,然后正常队列收消息,30min过期进入死信,然后直接消费,正好当延迟的功能了。

一个小问题:

当后面的消息时10s过期,前面的是30s过期时候,延迟功能会出现畏难而退,先发送30s的TTL消息,再发送10s的TTL消息,那么10s的TTL消息,会在30s过期之后,才进行处理。

采用延迟队列插件:

出现发送文件给docker内部,但是又无法使用拖拽模式的时候的办法(以及出现Enabling plugins on node rabbit@91e7e7949d70:
rabbitmq_delayed_message_exchange
Error:
{:plugins_not_found, [:rabbitmq_delayed_message_exchange]}报错的解决方法

我们虽然不能本地scp直接传递到docker容器内部,那么不妨就拆开,拆成两步,一步是发送文件到服务器本地

怎么发使用scp,看我之前写的那个使用MAC上传本地文件到Linux的那个章节,然后传递完本地后,可以使用docker命令

这句话的意思是,复制服务器本地的,同步到docker内部位置

rabbitmqL是你用docker ps查看到的容器名称,后面跟着路径即可

docker cp rabbitmq_delayed_message_exchange-3.13.0.ez rabbitmq:/opt/rabbitmq/plugins

我们第一要检查版本号(上面放了3.13的,要是3.8左右,就去自己找吧)

第二就是要看你是要安装的docker里面,还是本机的rabbitmq里面,很关键的这个问题,因为目录不一样

如果你是本地的rabbitmq 大概率plugins文件夹在usr/lib/rabbitmq/plugins

但是你要是docker的话是发现lib里面是没有rabbitmq这个文件夹的,所以我尝试半天后,查找到了,原来我们是需要用opt这个文件夹,我是用ls查看后,发现里面有rabbitmq这个文件夹,然后往后走是plugins文件夹。这也就是他的恶心之处,最后我们是可以检查到插件列表中出现的

问题二:UI 界面不显示消息条数

在docker容器内部使用哈

cd  /etc/rabbitmq/conf.d/
echo management_agent.disable_metrics_collector = false > management_agent.disable_metrics_collector.conf

问题三:Listener method could not be invoked with the incoming message和 Cannot convert from [java.lang.Strin 

包引入错误,一般是Channel

import com.rabbitmq.client.Channel;
import com.xuecheng.rabbitmq.config.RabbitmqConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

在官方给我们的延迟队列插件中,假如下面两个时间调换,那么也可以轻松变成以10s开始,30在后面。

@RequestMapping("/delay")
   public String delay(){
        System.out.println("delay");
        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay test 10s",message -> {
            message.getMessageProperties().setDelay(10000); //设置延迟时间
            return  message;
        });
        rabbitTemplate.convertAndSend(Constants.DELAY_EXCHANGE,"delay","delay test 30s",message -> {
            message.getMessageProperties().setDelay(30000);
            return  message;

        });
        System.out.printf("%tc 消息发送成功 \n",new Date());
        return "消息发送成功";
    }

介绍一下延时队列

消息发送后,并不立即给消费者,而是等待特定时间,才发送给消费者

1.订单在十分钟未支付自动取消(当然也不一定用延时队列,计时器,啥的其实都可以,每分钟扫描一下即可)

RabbitMQ并没直接实现延迟队列:
1.TTL+死信队列         (灵活,不需要插件,但是存在一个消息顺序问题,需要额外逻辑来处理死信队列的消息,增加系统的复杂性)

2.使用官方的延迟插件实现延迟功能

(优点,不存在消息顺序问题,也还比较灵活,通过插件,可以直接创建延迟队列,

缺点:有版本限制,需要特定插件)

 


总结 

死信队列:普通队列的底下保底,假如被消费端拒绝了,意味着消息会被丢到对应的死信队列中,相当于延长了队列了生命周期(因为假如被拒绝,就可能会被丢弃)。

使用channel.basicNack,并且此时requeue属性被设置成false.

消息被拒绝

消息在队列中存活时间超过设置的TTL时间

消息队列的消息数量已经超过最大队列长度

那么该消息会变成为死信,死信消息会被RabbitMQ进行特殊处理,如果配置了死心队列消息,那么消息将会被丢进死信队列中,如果没有配置,则该消息会被丢弃

为每个需要使用死信的业务配置一个死信交换机,这里同一个项目的私信交换机可以公用一个,然后为每个业务队列分配一个单独的路由Key,死信队列只不过是绑定在死信交换机上的队列,并非什么特殊交换机,只不过是接受死信的交换机,可以为任何类型【Direct.Fanout,Topic】

死信队列消息流转过程:

生产者->交换机->队列(拒绝消费)->死信交换机->死信队列

延时对列:我发送的消息,不希望他今天消费,希望他明天消费,如果设置时间没到,则不能被消费(定时发布)

TTL:一条消息或者该队列中所有消息的最大存活时间,如果一条消息设置了TTL属性或者进入设置TTL属性的队列,那么这条消息如果在TTL设置的时间内没有被消费,则会成为死信,如果同时配置了队列的TTL和消息的TTL,那么较小的那个值会被禁用(更清楚的表达:

存在两种方法设置过期时间,一种是给队列设置,这种设置后,整个队列都是相同的过期时间,另一种是给消息本身进行配置,这种设置后,每种消息过期时间不同,如果同时使用这两种方法,那么以过期时间小的那个数值为准)

队列设置:队列中声明使用x-message-url参数,单位为毫秒

单个消息设置:是设置消息属性的expiration参数的值,单位为毫秒

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狗哥不是甜妹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值