死信队列

概述

消息队列中的数据,如果迟迟没有消费者来处理,就会一直占用消息队列的空间。

比如抢车票的场景,用户下单高铁票之后,会进行抢座,然后再进行付款,但是如果用户下单之后并没有及时的付款,这张票不可能一直让这个用户占用着,因为这样别人就买不到这张票了,所以会在一段时间后超时,让这张票可以继续被其他人购买。

这时,就可以使用死信队列,将那些用户超时未付款的或是用户主动取消的订单,进行进一步的处理。

那么如何构建这样的一种使用模式呢?实际上本质就是一个死信交换机+死信队列

当正常队列中的消息被判定为死信时,会被发送到对应的死信交换机,然后再通过交换机发送到死信队列中,死信队列也有对应的消费者去处理消息。

判定为死信一般是3种情况:

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false
  • 消息超时未消费。
  • 消息队列达到最大长度。

产生死信

消息被拒绝

  1. 在配置类中创建一个新的死信交换机和死信队列,并进行绑定。

    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.Exchange;
    import org.springframework.amqp.core.ExchangeBuilder;
    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.core.QueueBuilder;
    import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration; /**
    * RabbitMQ配置类
    */
    @Configuration
    public class RabbitMqConfig { ... /**
    * 定义消息队列
    * @return 消息队列对象
    */
    @Bean("testQueue")
    public Queue queue(){
    return QueueBuilder
    // 非持久化类型
    .nonDurable("test_springboot")
    // 指定死信交换机
    .deadLetterExchange("dl.direct")
    // 指定死信RoutingKey
    .deadLetterRoutingKey("dl_test_springboot_key")
    .build();
    } /**
    * 构建死信交换机
    * @return 死信交换机
    */
    @Bean
    public Exchange dlExchange(){
    // 创建一个新的死信交换机
    return ExchangeBuilder.directExchange("dl.direct").build();
    } /**
    * 构建死信队列
    * @return 死信队列
    */
    @Bean
    public Queue dlQueue(){
    return QueueBuilder
    .nonDurable("dl_test_springboot")
    .build();
    } /**
    * 死信交换机和死信队列绑定
    * @param exchange 死信交换机
    * @param queue 死信队列
    * @return 绑定对象
    */
    @Bean
    public Binding dlBinding(@Qualifier("dlExchange") Exchange exchange,
    @Qualifier("dlQueue") Queue queue){
    return BindingBuilder
    .bind(queue)
    .to(exchange)
    .with("dl_test_springboot_key")
    .noargs();
    } ... }
  2. 监听正常队列和死信队列消息。

    import cn.codesail.rabbitmq.entity.User;
    import com.rabbitmq.client.Channel;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.amqp.support.AmqpHeaders;
    import org.springframework.messaging.handler.annotation.Header;
    import org.springframework.stereotype.Component; /**
    * 直连队列监听器
    */
    @Component
    public class DirectListener { /**
    * 监听正常队列消息
    */
    @RabbitListener(queues = "test_springboot", messageConverter = "jackson2JsonMessageConverter")
    public void receiver(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, User user) throws Exception {
    // 拒绝消息。第二个参数为true则消息返回队列,第二个参数为false则消息不返回队列,成为死信
    channel.basicReject(deliveryTag, false);
    System.out.println("正常队列接收到消息:" + user);
    } /**
    * 监听死信队列消息
    */
    @RabbitListener(queues = "dl_test_springboot", messageConverter = "jackson2JsonMessageConverter")
    public void receiverDl(User user) {
    System.out.println("死信队列接收到消息:" + user);
    }
    }

    正常队列消息的监听种拒绝了消息,且不返回队列,成为了死信,就会被死信队列的监听接收到。

  3. 删除原队列。删除了原队列才能创建与死信队列绑定的队列。

  4. 实现生产者。

    import cn.codesail.rabbitmq.entity.User;
    import org.junit.jupiter.api.Test;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest
    class RabbitMqSpringBootTests { /**
    * RabbitTemplate封装了大量的RabbitMQ操作,已经由Starter提供,因此直接注入使用即可
    */
    @Autowired
    private RabbitTemplate rabbitTemplate; /**
    * 生产者
    */
    @Test
    void producer() { // 发送Json消息
    User user = new User();
    user.setName("张三");
    user.setAge(18);
    rabbitTemplate.convertAndSend("amq.direct", "test_springboot_key", user);
    } }
  5. 启动生产者发送消息:

    可以看到,死信队列接收到了消息。

消息超时未消费

  1. 设定队列TTL值。

    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.core.QueueBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration; /**
    * RabbitMQ配置类
    *
    * @author CodeSail
    */
    @Configuration
    public class RabbitMqConfig { ... /**
    * 定义消息队列
    * @return 消息队列对象
    */
    @Bean("testQueue")
    public Queue queue(){
    return QueueBuilder
    // 非持久化类型
    .nonDurable("test_springboot")
    // 指定死信交换机
    .deadLetterExchange("dl.direct")
    // 指定死信RoutingKey
    .deadLetterRoutingKey("dl_test_springboot_key")
    // 如果5秒没处理,就自动删除
    .ttl(5000)
    .build();
    } ...
    }
  2. 取消正常队列监听。

    import cn.codesail.rabbitmq.entity.User;
    import com.rabbitmq.client.Channel;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.amqp.support.AmqpHeaders;
    import org.springframework.messaging.handler.annotation.Header;
    import org.springframework.stereotype.Component; /**
    * 直连队列监听器
    */
    @Component
    public class DirectListener { /**
    * 监听正常队列消息
    */
    // @RabbitListener(queues = "test_springboot", messageConverter = "jackson2JsonMessageConverter")
    // public void receiver(Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, User user) throws Exception {
    // // 拒绝消息。第二个参数为true则消息返回队列,第二个参数为false则消息不返回队列,成为死信
    // channel.basicReject(deliveryTag, false);
    // System.out.println("正常队列接收到消息:" + user);
    // } /**
    * 监听死信队列消息
    */
    @RabbitListener(queues = "dl_test_springboot", messageConverter = "jackson2JsonMessageConverter")
    public void receiverDl(User user) {
    System.out.println("死信队列接收到消息:" + user);
    }
    }
  3. 实现生产者。

    import cn.codesail.rabbitmq.entity.User;
    import org.junit.jupiter.api.Test;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import java.util.concurrent.TimeUnit; @SpringBootTest
    class RabbitMqSpringBootTests { /**
    * RabbitTemplate封装了大量的RabbitMQ操作,已经由Starter提供,因此直接注入使用即可
    */
    @Autowired
    private RabbitTemplate rabbitTemplate; /**
    * 生产者
    */
    @Test
    void producer() throws InterruptedException { // 发送Json消息
    User user = new User();
    user.setName("张三");
    user.setAge(18);
    rabbitTemplate.convertAndSend("amq.direct", "test_springboot_key", user);
    } }
  4. 删除原队列。删除了原队列才能创建与死信队列绑定的设定了TTL的队列。

  5. 启动服务,监听消息。

  6. 启动生产者发送消息,等待5秒:

    可以看到,死信队列接收到了消息。

消息队列达到最大长度

  1. 设置队列最大消息长度。

    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.Exchange;
    import org.springframework.amqp.core.ExchangeBuilder;
    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.core.QueueBuilder;
    import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration; /**
    * RabbitMQ配置类
    */
    @Configuration
    public class RabbitMqConfig { ... /**
    * 定义消息队列
    * @return 消息队列对象
    */
    @Bean("testQueue")
    public Queue queue(){
    return QueueBuilder
    // 非持久化类型
    .nonDurable("test_springboot")
    // 指定死信交换机
    .deadLetterExchange("dl.direct")
    // 指定死信RoutingKey
    .deadLetterRoutingKey("dl_test_springboot_key")
    // 最大长度设定为3
    .maxLength(3)
    .build();
    } ... }
  2. 取消正常队列监听。

    import cn.codesail.rabbitmq.entity.User;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component; /**
    * 直连队列监听器
    */
    @Component
    public class DirectListener { /**
    * 监听正常队列消息
    */
    // @RabbitListener(queues = "test_springboot", messageConverter = "jackson2JsonMessageConverter")
    // public void receiver(User user) {
    // System.out.println("正常队列接收到消息:" + user);
    // } /**
    * 监听死信队列消息
    */
    @RabbitListener(queues = "dl_test_springboot", messageConverter = "jackson2JsonMessageConverter")
    public void receiverDl(User user) {
    System.out.println("死信队列接收到消息:" + user);
    }
    }
  3. 删除原队列。删除了原队列才能创建与死信队列绑定的设定了最大长度的队列。

  4. 定义生产者。

    import cn.codesail.rabbitmq.entity.User;
    import org.junit.jupiter.api.Test;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import java.util.concurrent.TimeUnit; @SpringBootTest
    class RabbitMqSpringBootTests { /**
    * RabbitTemplate封装了大量的RabbitMQ操作,已经由Starter提供,因此直接注入使用即可
    */
    @Autowired
    private RabbitTemplate rabbitTemplate; /**
    * 生产者
    */
    @Test
    void producer() throws InterruptedException { for (int i = 0; i < 4; i++) {
    User user = new User();
    user.setName("张三" + i);
    user.setAge(18);
    rabbitTemplate.convertAndSend("amq.direct", "test_springboot_key", user);
    }
    } }
  5. 启动生产者发送消息。

    可以看到,队列的第一个元素被挤出成为了死信。

    队列就类似于一个管道,当管道的人占满了,最后进去的人就会把最前面的人挤出去。


RabbitMQ 11 死信队列的更多相关文章

  1. RabbitMQ实战-死信队列

    RabbitMQ死信队列 场景说明 代码实现 简单的Util 生产者 消费者 场景说明 场景: 当队列的消息未正常被消费时,如何解决? 消息被拒绝并且不再重新投递 消息超过有效期 队列超载 方案: 未 ...

  2. RabbitMQ配置死信队列

    死信队列 消息传输过程中难免会产生一些无法及时处理的消息,这些暂时无法处理的消息有时候也是需要被保留下来的,于是这些无法被及时处理的消息就变成了死信. 既然需要保留这些死信,那么就需要一个容器来存储它 ...

  3. RabbitMQ之死信队列

    1:何为死信队列 死信队列也是一个正常的队列,可以被消费. 但是,死信队列的消息来源于其他队列的转发. 2:如何触发死信队列 1:消息超时 2:队列长度达到极限 3:消息被拒绝消费,并不再重进队列,且 ...

  4. RabbitMQ死信队列另类用法之复合死信

    前言 在业务开发过程中,我们常常需要做一些定时任务,这些任务一般用来做监控或者清理任务,比如在订单的业务场景中,用户在创建订单后一段时间内,没有完成支付,系统将自动取消该订单,并将库存返回到商品中,又 ...

  5. 【RabbitMQ】一文带你搞定RabbitMQ死信队列

    本文口味:爆炒鱿鱼   预计阅读:15分钟 一.说明 RabbitMQ是流行的开源消息队列系统,使用erlang语言开发,由于其社区活跃度高,维护更新较快,性能稳定,深得很多企业的欢心(当然,也包括我 ...

  6. netcore下死RabbitMQ队列、死信队列、延时队列及小应用

    关于安装rabbitmq这里一笔掠过了. 下面进入正题: 1.新建aspnetcorewebapi空项目,NormalQueue,删除controllers文件夹已经无关的文件,这里为了偷懒不用con ...

  7. 《RabbitMQ》什么是死信队列

    一 什么是死信队列 当一条消息在队列中出现以下三种情况的时候,该消息就会变成一条死信. 消息被拒绝(basic.reject / basic.nack),并且requeue = false 消息TTL ...

  8. rabbitmq实现延时队列(死信队列)

    基于队列和基于消息的TTL TTL是time to live 的简称,顾名思义指的是消息的存活时间.rabbitMq可以从两种维度设置消息过期时间,分别是队列和消息本身. 队列消息过期时间-Per-Q ...

  9. RabbitMQ 死信队列 延时

    package com.hs.services.config; import java.util.HashMap; import java.util.Map; import org.springfra ...

  10. 关于 RabbitMQ 的 Dead-Letters-Queue “死信队列”

      来自一个队列的消息可以被当做‘死信’,即被重新发布到另外一个“exchange”去,这样的情况有: 消息被拒绝 (basic.reject or basic.nack) 且带 requeue=fa ...

随机推荐

  1. 【Azure 应用服务】Azure Function 中运行Powershell 脚本,定位 -DefaultProfile 引发的错误

    问题描述 突然之间,使用PowerShell脚本 Get-AzVirtualNetwork 获取虚拟网络信息时,如果带上  -DefaultProfile $sub 参数,就出现 Azure cred ...

  2. 2022年RPA行业发展十大趋势,六千字长文助你看懂RPA

    2022年RPA行业发展十大趋势,六千字长文助你看懂RPA 2022年RPA行业如何发展?十大趋势助你看懂RPA行业未来 这里有2022年RPA行业发展的十大趋势,关注RPA的朋友定要收藏! 文/王吉 ...

  3. RIPEMD算法:多功能哈希算法的瑰宝

    一.RIPEMD算法的起源与历程 RIPEMD(RACE Integrity Primitives Evaluation Message Digest)算法是由欧洲研究项目RACE发起,由Hans D ...

  4. 实现一个 SEO 友好的响应式多语言官网 (Vite-SSG + Vuetify3) 我的踩坑之旅

    在 2023 年的年底,我终于有时间下定决心把我的 UtilMeta 项目官网 进行翻新,主要的原因是之前的官网是用 Vue2 实现的一个 SPA 应用,对搜索引擎 SEO 很不友好,这对于介绍项目的 ...

  5. 接入移动手机号一键登录类的封装,app应用,php服务端类的封装与调用

    需求:实现手机号一键登录,由于官方只有java的demo和jar包,没有php的sdk及demo <?php/* * 手机号一键登录加解密 */class Autophone{ const A_ ...

  6. ThinkPHP 3.2.3

    说明手册 https://www.kancloud.cn/manual/thinkphp/1706 下载地址 https://gitee.com/liu21st/thinkphp32 thinkPHP ...

  7. 记一次maven不下来的经历

    起因:自己手动搭建个项目,参考公司项目使用了很多依赖,但是当自己maven时候发现一个依赖怎么也down不下来,就此展开了一番折腾 这个依赖叫 <dependency> <group ...

  8. FFmpeg介绍与编译

    目录 FFmpeg FFmpeg核心模块 FFmpeg编译 FFmpeg FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制 ...

  9. NA嵌入Flutter页面

    目录介绍 01.Android承载flutter容器 02.过时的NA跳转flutter方案 03.升级版本NA跳转Flutter处理 04.如何处理NA跳转flutter传参 05.思考遇到的几个问 ...

  10. npm install安装依赖包时报错npm ERR! command C:\Windows\system32\cmd.exe /d /s /c node install.js,npm ERR! ChromeDriver installation failed Error with http(s) request: Error: read ECONNRESET

    PS E:\20231213\uirecorder> PS E:\20231213\uirecorder> PS E:\20231213\uirecorder> PS E:\2023 ...