死信队列理解与使用

news2024/11/26 10:40:49

一、简介

在rabbitMQ中常用的交换机有三种,直连交换机、广播交换机、主题交换机;

直连交换机中队列与交换机需要约定好routingKey去进行绑定;

广播交换机并不需要routingKey绑定,只需队列与交换机绑定即可;

主题交换机最大的特点可以通过*和#去匹配队列;

而死信队列其实就是平常的队列的一种,通常我会使用直连交换机来作为死信队列;所以说,死信队列其实就是我们在处理业务中慢慢衍生出来的一个名词、一种方案;它和普通的队列是一样的。

二、使用场景

        我们知道在使用队列时有几种应答模式,比如自动应答(auto)、手动应答(manual)等,而在使用自动应答时,无论消息是否成功消费,达到重试次数后就会自动的把此消息给删除掉了,当然我们是不想把没有消费成功的消息给删除掉的。而开启手动应答时,配置的重试机制会失效 当有消费失败的消息时 会进入死循环。

        那么为了解决此场景,就引入了死信队列。当有不能正常消费的消息时 就把此消息给打到死信队列中,然后再根据实际情况去处理此信息。

关于自动应答和手动应答可参考这篇博客: 

rabbitMQ手动应答与自动应答_骑着蜗牛打天下的博客-CSDN博客

 

在 RabbitMQ 中充当主角的就是消息,在不同场景下,消息会有不同地表现。

死信就是消息在特定场景下的一种表现形式,这些场景包括:

1. 消息被拒绝访问,即 RabbitMQ返回 basicNack 的信号时。 或者拒绝basicReject

2. 消费者发生异常,超过重试次数 。

3. 消息的 TTL 过期时。

4. 消息队列达到最大长度。

三、代码实现

父pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.1</version>
<!--        <version>2.2.5.RELEASE</version>-->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.chensir</groupId>
    <artifactId>spring-boot-rabbitmq</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-rabbitmq</name>

    <properties>
        <java.version>8</java.version>
        <hutool.version>5.8.3</hutool.version>
        <lombok.version>1.18.24</lombok.version>
    </properties>

    <description>spring-boot-rabbitmq</description>

    <packaging>pom</packaging>

    <modules>
        <module>direct-exchange</module>
        <module>fanout-exchange</module>
        <module>topic-exchange</module>
        <module>game-exchange</module>
        <module>dead-letter-queue</module>
        <module>delay-queue</module>
        <module>delay-queue2</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>


</project>

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.chensir</groupId>
        <artifactId>spring-boot-rabbitmq</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>dead-letter-queue</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件

server.port=8084
#host
spring.rabbitmq.host=121.40.100.66
#默认5672
spring.rabbitmq.port=5672
#用户名
spring.rabbitmq.username=guest
#密码
spring.rabbitmq.password=guest
#连接到代理时用的虚拟主机
spring.rabbitmq.virtual-host=/
#每个消费者每次可最大处理的nack消息数量
spring.rabbitmq.listener.simple.prefetch=1
#表示消息确认方式,其有三种配置方式,分别是none、manual(手动)和auto(自动);默认auto
spring.rabbitmq.listener.simple.acknowledge-mode=auto
#监听重试是否可用
spring.rabbitmq.listener.simple.retry.enabled=true
#最大重试次数
spring.rabbitmq.listener.simple.retry.max-attempts=5
#最大重试时间间隔
spring.rabbitmq.listener.simple.retry.max-interval=3000ms
#第一次和第二次尝试传递消息的时间间隔
spring.rabbitmq.listener.simple.retry.initial-interval=1000ms
#应用于上一重试间隔的乘数
spring.rabbitmq.listener.simple.retry.multiplier=2
#决定被拒绝的消息是否重新入队;默认是true(与参数acknowledge-mode有关系)
spring.rabbitmq.listener.simple.default-requeue-rejected=false

config

正常队列config

package com.chensir.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {

    @Bean
    public MessageConverter messageConverter(){
        return  new Jackson2JsonMessageConverter();
    }

    @Bean
    public DirectExchange directExchange(){
        return  new DirectExchange("DirectExchange",true,false);
    }

    @Bean
    public Queue directQueueLong(){
        return   QueueBuilder.durable("DirectQueue")
                .deadLetterExchange("DeadLetterExchange")
                .deadLetterRoutingKey("dead")
                //20s还没消费就打到死信队列中
                .ttl(20000)
                //当队列中长度有500个消息,也打入死信队列
                .maxLength(500)
                .build();
    }

    @Bean
    public Binding binding(){
        return  BindingBuilder.bind(directQueueLong()).to(directExchange()).with("direct123");
    }
}

死信队列config

package com.chensir.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 死信队列 一般由运维在rebbitMQ服务创建交换机和队列 不需要代码配置
 */
//@Configuration
public class DeadLetterConfig {
    @Bean
    public MessageConverter messageConverter(){

        return  new Jackson2JsonMessageConverter();
    }

    @Bean
    public DirectExchange directExchange() {
        DirectExchange directExchange = new DirectExchange("DeadLetterExchange");
        return directExchange;
    }

    @Bean
    public Queue queue() {
        Queue deadLetterQueue = QueueBuilder.durable("DeadLetterQueue").build();
        return deadLetterQueue;
    }

    @Bean
    public Binding binding() {
        Binding binding = BindingBuilder.bind(queue()).to(directExchange()).with("dead");
        return binding;
    }


}

生产者

package com.chensir.provider;

import cn.hutool.json.JSONUtil;
import com.chensir.model.OrderIngOk;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class DirectProvider {

    @Resource
    private RabbitTemplate rabbitTemplate;


    public  void  send(){

        // 死信队列
//        rabbitTemplate.convertAndSend("DeadLetterExchange", "dead","123");

        for (int i=1;i<7;i++){
            OrderIngOk orderIngOk = new OrderIngOk();
            orderIngOk.setOrderNo("202308289687-"+i);
            orderIngOk.setId(i);
            orderIngOk.setUserName("倪海杉");
//            String s = JSONUtil.toJsonStr(orderIngOk);
            rabbitTemplate.convertAndSend("DirectExchange", "direct123",orderIngOk);
        }


    }

}

消费者

正常队列消费者

package com.chensir.consumer;

import cn.hutool.json.JSONUtil;
import com.chensir.model.OrderIngOk;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(queues = "DirectQueue" )
    public void  process(OrderIngOk orderIngOk) throws IOException {

        try {
           // 处理业务开始
            if(orderIngOk.getId().equals(5)){

                int a =  0;
                int b= 2/a;
            }
            System.out.println("接受到消息,并正常处理结束"+ JSONUtil.toJsonStr(orderIngOk));
        } catch (Exception ex){
            System.out.println(ex.getMessage());
            System.out.println("接受到消息,发生异常"+ JSONUtil.toJsonStr(orderIngOk));
            //自动应答 当消费者成功消费消息时会自动把消息删除,而没有成功消费消息时需要给重试机制抛出个异常 重试机制才会开启重试
            throw ex;
            //手动模式
            //channel.basicReject(deliveryTag,true);
            //channel.basicNack(deliveryTag,false,true);
        }
    }
}

死信队列消费者

package com.chensir.consumer;

import com.chensir.model.OrderIngOk;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class DeadConsumer {
    @RabbitHandler
    @RabbitListener(queues = "DeadLetterQueue")
    public void  process(OrderIngOk orderIngOk)  {

        System.out.println("这条信息在运行时发生了未知的异常,此信息被打到了死信队列,被死信队列消费者消费成功"+orderIngOk);
    }
}

结果

接受到消息,并正常处理结束{"id":1,"OrderNo":"202308289687-1","userName":"倪海杉"}
接受到消息,并正常处理结束{"id":2,"OrderNo":"202308289687-2","userName":"倪海杉"}
接受到消息,并正常处理结束{"id":3,"OrderNo":"202308289687-3","userName":"倪海杉"}
接受到消息,并正常处理结束{"id":4,"OrderNo":"202308289687-4","userName":"倪海杉"}
/ by zero
接受到消息,发生异常{"id":5,"OrderNo":"202308289687-5","userName":"倪海杉"}
/ by zero
接受到消息,发生异常{"id":5,"OrderNo":"202308289687-5","userName":"倪海杉"}
/ by zero
接受到消息,发生异常{"id":5,"OrderNo":"202308289687-5","userName":"倪海杉"}
/ by zero
接受到消息,发生异常{"id":5,"OrderNo":"202308289687-5","userName":"倪海杉"}
/ by zero
接受到消息,发生异常{"id":5,"OrderNo":"202308289687-5","userName":"倪海杉"}
2023-08-28 16:45:39.848  WARN 24432 --- [ntContainer#1-1] o.s.a.r.r.RejectAndDontRequeueRecoverer  : Retries exhausted for message (Body:'[B@1a6e663a(byte[58])' MessageProperties [headers={__TypeId__=com.chensir.model.OrderIngOk}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=DirectExchange, receivedRoutingKey=direct123, deliveryTag=5, consumerTag=amq.ctag-f9Up1UES-F3rDvb-AK16xw, consumerQueue=DirectQueue])


这条信息在运行时发生了未知的异常,此信息被打到了死信队列,被死信队列消费者消费成功OrderIngOk(id=5, OrderNo=202308289687-5, userName=倪海杉)
接受到消息,并正常处理结束{"id":6,"OrderNo":"202308289687-6","userName":"倪海杉"}

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

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

相关文章

dml dql 约束 数据库设计

dml 1.添加数据 *语法 &#xff1a; *insert into 表名&#xff08;列名1&#xff0c;列名2&#xff0c;……..列名n&#xff09;values(值1&#xff0c;值2&#xff0c;…值n) *注意&#xff1a; 1.列名和值要一一对应 2.如果表名后&#xff0c;不写列名&#xff0c;就要给…

QT 获取程序编译时间并且转化格式

前言 有的时候我们要在程序的关于界面加上程序的编译时间等信息。可以通过如下代码实现 QString buildTime QStringLiteral("%1 %2").arg(__DATE__).arg(__TIME__);ui->label->setText(buildTime);QDateTime dateTime QLocale("en_US").toDateTim…

easyx图形库6:实现飞机大战:

飞机大战 一.飞机大战的基本窗体&#xff1a;二.飞机大战的主要战斗场景元素&#xff1a;一.透明图片&#xff1a;二.精灵对象&#xff1a; 三.主游戏场景的元素&#xff1a;1.英雄飞机的结构体定义&#xff1a;2.英雄飞机的代码实现&#xff1a;3.游戏背景的结构体定义&#x…

Day5:react函数组件与类组件

「目标」: 持续输出&#xff01;每日分享关于web前端常见知识、面试题、性能优化、新技术等方面的内容。 「主要面向群体&#xff1a;」前端开发工程师&#xff08;初、中、高级&#xff09;、应届、转行、培训、自学等同学 Day4-今日话题 react「函数组件和类组件」的区别&…

查局域网所有占用IP

查局域网所有占用IP 按&#xff1a;winr 出现下面界面&#xff0c;在文本框中输入 cmd 按确定即可出现cmd命令界面 在cmd命令窗口输入你想要ping的网段&#xff0c;下面192.168.20.%i即为你想要ping的网段&#xff0c;%i代表0-255 for /L %i IN (1,1,254) DO ping -w 1 -n 1…

(动态规划) 剑指 Offer 49. 丑数 ——【Leetcode每日一题】

❓剑指 Offer 49. 丑数 难度&#xff1a;中等 我们把只包含质因子 2、3 和 5 的数称作丑数&#xff08;Ugly Number&#xff09;。求按从小到大的顺序的第 n 个丑数。 示例: 输入: n 10 输出: 12 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 说明: 1 是丑数。…

2024年新iPad Pro将实现6年来最大的升级

彭博社的Mark Gurman长期以来一直将iPad Pro的下一次重大更新定在2024年&#xff0c;在最新一期的Power On时事通讯中&#xff0c;他详细阐述了一些细节&#xff0c;这些细节将使其成为“自2018年以来该产品的首次重大更新” 尽管Gurman将最近的iPad升级描述为“最近特别小”&…

word6 图文混排

目录 7-1 段落缩进排版7-2 搞定多级列表难题 7-1 段落缩进排版 段落对齐 缩进问题 悬挂缩进&#xff1a;缩进首行以外的段落 段落对齐&#xff1a; 7-2 搞定多级列表难题

k8s 安装 kubernetes安装教程 虚拟机安装k8s centos7安装k8s kuberadmin安装k8s k8s工具安装 k8s安装前配置参数

k8s采用master, node1, node2 。三台虚拟机安装的一主两从&#xff0c;机器已提前安装好docker。下面是机器配置&#xff0c;k8s安装过程&#xff0c;以及出现的问题与解决方法 虚拟机全部采用静态ip, master 30机器, node1 31机器, node2 32机器 机器ip 192.168.164.30 # ma…

【数据结构与算法篇】手撕八大排序算法之快排的非递归实现及递归版本优化(三路划分)

​&#x1f47b;内容专栏&#xff1a; 《数据结构与算法篇》 &#x1f428;本文概括&#xff1a; 利用数据结构栈(Stack)来模拟递归&#xff0c;实现快排的非递归版本&#xff1b;递归版本测试OJ题时&#xff0c;有大量重复元素样例不能通过&#xff0c;导致性能下降&#xff0…

Self-Supervised Learning

Self-Supervised Learning Bert 的数据是 340M parameters 抽象解释&#x1f446; Bert 单个字的预测 把一个字盖住&#xff1a; 1、把一个字替换成特殊字符(MASK)。 2、替换成随机的一个字,进行训练。 next sentence prediction 通过变换两个连起来的句子的顺序&#x…

C. Battle 2023 (ICPC) Jiangxi Provincial Contest -- Official Contest

Problem - C - Codeforces 题目大意&#xff1a;有n堆石子&#xff0c;给出一个数p&#xff0c;A先B后&#xff0c;每个人每次只能取p的幂个石子&#xff08;包括1&#xff09;问A能不能赢 1<n<3e5;1<p<1e18 思路&#xff1a;先递归算出sg函数看看&#xff0c;s…

detour编译问题及导入visual studio

Detours是经过微软认证的一个开源Hook库&#xff0c;Detours在GitHub上&#xff0c;网址为 https://github.com/Microsoft/Detours 注意版本不一样的话也是会出问题的&#xff0c;因为我之前是vs2022的所以之前的detours.lib不能使用&#xff0c;必须用对应版本的x64 Native To…

线性代数的艺术

推荐一本日本网友Kenji Hiranabe写的《线性代数的艺术》。这本书是基于MIT大牛Gilbert Strang教授的《每个人的线性代数》制作的。 虽然《线性代数的艺术》这本书仅仅只有12页的内容&#xff0c;就把线性代数的重点全画完了&#xff0c;清晰明了。 《线性代数的艺术》PDF版本&…

C语言:指针类型的意义

1.指针的类型决定了解引用时访问几个字节 2.指针的类型决定了指针1、-1跳过几个字节 一、指针的类型决定指针解引用时访问几个字节 例如 int 型指针解引用时访问4个字节 char 型指针解引用时访问1个字节 详解代码如下&#xff1a; int b 0x11223344&#xff08;十六进制&…

通过这 5 项 ChatGPT 创新增强您的见解

为什么绝大多数的人还不会使用chatGPT来提高工作效能&#xff1f;根本原因就在还不会循序渐进的发问与chatGPT互动。本文总结了5个独特的chatGPT提示&#xff0c;可以帮助您更好地与Chat GPT进行交流&#xff0c;以获得更清晰的信息、额外的信息和见解。 澄清假设和限制 用5种提…

2023-8-28 n-皇后问题

题目链接&#xff1a;n-皇后问题 第一种搜索顺序 #include <iostream>using namespace std;const int N 20;int n; char g[N][N]; bool row[N], col[N], dg[N], udg[N];void dfs(int x, int y, int s) {if(y n) y 0, x ;if(x n){if(s n){for(int i 0; i < n;…

水源井监控系统整体解决方案

1.1、系统组成水源井远程监控系统主要由监控中心、通信平台、水源井测控终端、现场启动柜设备组成。系统整体结构按功能可划分为采集层、网络层和应用层等三层&#xff0c;水源井测控终端主要实现采集层的功能&#xff0c;数据传输链路主要实现网络层的功能&#xff0c;中心端管…

ModaHub魔搭社区:将图像数据添加至Milvus Cloud向量数据库中

将图像数据添加至向量数据库中 图像分割裁剪完成后,我们就可以将其添加至 Milvus Cloud 向量数据库中了。为了方便上手,本项目中使用了 Milvus Lite 版本,可以在 notebook 中运行 Milvus 实例。接下来,使用 PyMilvus 连接至 Milvus Lite 提供的默认服务器。 这一步骤中,…