第十四章 RabbitMQ延迟消息之延迟队列

目录

一、引言

二、死信队列

三、核心代码实现

四、运行效果 

五、总结


一、引言

什么是延迟消息?

发送者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间后收到消息。

什么是延迟任务?

设置在一定时间之后才执行的任务。

延迟消息使用场景

我们在实际项目中经常会有一些场景,需要延迟指定时间后发送消息,比如在电商或者外卖平台中订单10分钟后自动取消功能等。

对于上述延迟消息的场景,我们该怎么实现呢?

RabbitMQ 官方并没有直接内置延迟消息的功能,但是可以通过 TTL(Time-To-Live)和死信队列(Dead Letter Exchanges)的组合来实现延迟消息的效果,另外RabbitMQ 也可以通过安装延迟消息插件的方式来实现。

二、死信队列

电商购物中,针对用户下单扣减库存的服务逻辑,我们希望删除10分钟后状态为未支付的订单。在过去的项目中,我们可能第一时间会想到通过定时任务定期查询未支付的订单并做删除来实现:

定时任务会有两个问题:

1. 当针对订单量特别大的电商项目而言,定时任务间断性地查询整个订单数据会极大增加订单服务的压力。

2. 定时任务存在时间上的滞后性。

通过使用RabbitMQ延迟消息,我们可以在完成需求的同时,有效的避免上述问题。如下图所示,用户通过交易服务下单(状态为未支付),随后交易服务调用商品服务扣减库存。 用户在调用交易服务的同时发送一个延迟消息到RabbitMQ,10分钟后交易服务收到消息,此时如果订单还是未支付状态,则取消订单。

RabbitMQ中的死信队列,就是一种可以实现延迟消息的方式。当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter):

1. 消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false

2. 消息是一个过期消息(达到了队列或消息本身设置的过期时间),超时无人消费

3. 要投递的队列消息堆积满了,最早的消息可能成为死信

如果队列通过dead-letter-exchange属性指定了一个交换机,那么该队列中的死信就会投递到这个交换机中。这个交换机称为死信交换机(Dead Letter Exchange,简称DLX)。

三、核心代码实现

package com.example.consumer;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 常规的RabbitMQ 交换机/队列绑定配置类
 */
@Configuration
public class RabbitMQConfig {

    @Bean
    Queue normalQueue() {
        // 使用 QueueBuilder 创建一个持久化队列
        return QueueBuilder.durable("normal.queue")
                .deadLetterExchange("dead.direct")
                .deadLetterRoutingKey("dead")
                .build();
    }

    @Bean
    DirectExchange normalDirect() {
        return ExchangeBuilder.directExchange("normal.direct").build();
    }

    @Bean
    Binding bindingNormal(Queue normalQueue, DirectExchange normalDirect) {
        return BindingBuilder.bind(normalQueue).to(normalDirect).with("normal");
    }

    @Bean
    Queue deadQueue() {
        // 使用 QueueBuilder 创建一个持久化队列
        return QueueBuilder.durable("dead.queue").build();
    }

    @Bean
    DirectExchange deadDirect() {
        return ExchangeBuilder.directExchange("dead.direct").build();
    }

    @Bean
    Binding bindingDead(Queue deadQueue, DirectExchange deadDirect) {
        return BindingBuilder.bind(deadQueue).to(deadDirect).with("dead");
    }
}
package com.example.publisher;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;

import java.nio.charset.StandardCharsets;

/**
 * 生产者
 */
@Slf4j
@SpringBootTest
class PublisherApplicationTests {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Test
    void test() {
        String content = "生活不易,所以保持足够的努力,对自己要有信心,积极地去面对工作生活的挑战!";
        Message message = MessageBuilder.withBody(content.getBytes(StandardCharsets.UTF_8))
                .setExpiration("10000").build();
        rabbitTemplate.convertAndSend("normal.direct",
                "normal", message);
    }
}
package com.example.consumer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

/**
 * 消费者
 */
@Slf4j
@Component
public class SimpleListener {

    @RabbitListener(queues = "dead.queue")
    public void listener1(Message message) throws Exception {
        String msg = new String(message.getBody(), StandardCharsets.UTF_8); ;
        System.out.println("消费者1:人生是个不断攀登的过程【" + msg + "】");
    }
}

四、运行效果 

五、总结

虽然我们通过RabbitMQ的死信队列能够实现延迟消息的功能,但是通过代码我们可以看到,这种实现方式相对来说比较繁琐。而且关键是RabbitMQ提供死信队列的初衷并不是让我们用来发送延迟消息的,而是为了作为兜底方案,来接收没有消费的死信的,便于定位问题。因此,后续章节会给大家讲解更优的解决方案,即延迟插件。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt入门教程:创建我的第一个小程序

本章教程,主要介绍如何编写一个简单的QT小程序。主要是介绍创建项目的过程。 一、打开QT软件编辑器 这里使用的是QT5.14.2版本的,安装教程参考以往教程:https://blog.csdn.net/qq_19309473/article/details/142907096 二、创建项目 到这里&am…

使用Docker部署nextjs应用

最近使用nextjs网站开发,希望使用docker进行生产环境的部署,减少环境的依赖可重复部署操作。我采用的是Dockerfile编写应用镜像方式 docker-compose实现容器部署的功能。 Docker Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器…

【大模型问答测试】大模型问答测试脚本实现(第一版)

背景 公司已经做了一段时间的大模型,每次测试或者回归的时候都需要针对问答进行测试回归,耗费大量的时间与精力,因此结合产品特点,开发自动化脚本替代人工的操作,提升测试回归效率 设计 使用pythonrequestExcel进行…

Android笔记(二十四)基于Compose组件的MVVM模式和MVI模式的实现

仔细研究了一下MVI(Model-View-Intent)模式,发现它和MVVM模式非常的相识。在采用Android JetPack Compose组件下,MVI模式的实现和MVVM模式的实现非常的类似,都需要借助ViewModel实现业务逻辑和视图数据和状态的传递。在这篇文章中&#xff0c…

易我数据恢复软件,一键找回你的重要资料!

我们生活在数字时代,数据对我们来说超级重要。工作文件、学习资料,还有照片视频,这些东西要是没了或者不小心删了,那得多烦人啊。幸好现在科技发达,有了数据恢复软件,就像给我们数据上了一把安全锁。市面上…

一篇闪击常用放大器电路(学习笔记)

文章目录 声明概念名词经典电路分析反向放大器同向放大器加法器减法器积分电路微分电路差分放大电路电流->电压转换电路电压->电流转换电路 虚短与虚断一、虚短二、虚断 一些碎碎念 声明 ​ 本文是主要基于以下两篇博客所做的笔记: 模电四:基本放…

IT招聘乱象的全面分析

近年来,IT行业的招聘要求似乎越来越苛刻,甚至有些不切实际。许多企业在招聘时,不仅要求前端工程师具备UI设计能力,还希望后端工程师精通K8S服务器运维,更有甚至希望研发经理掌握所有前后端框架和最新开发技术。这种招聘…

MySQL基本语法、高级语法知识总结以及常用语法案例

MySQL基本语法总结 MySQL是一种广泛使用的关系型数据库管理系统,其基本语法涵盖了数据库和数据表的创建、查询、修改和删除等操作。 一、数据库操作 创建数据库(CREATE DATABASE) 语法:CREATE DATABASE [IF NOT EXISTS] databa…

最新PHP礼品卡回收商城 点卡回收系统源码_附教程

最新PHP礼品卡回收商城 点卡回收系统源码_附教程 各大电商平台优惠券秒杀拼团限时折扣回收商城带余额宝 1、余额宝理财 2、回收、提现、充值、新订单语音消息提醒功能 3、带在线客服 4、优惠券回收功能 源码下载:https://download.csdn.net/download/m0_66047…

Android实现App内直接预览本地PDF文件

在App内实现直接预览pdf文件,而不是通过调用第三方软件,如WPS office等打开pdf。 主要思路:通过PhotoView将pdf读取为图片流进行展示。 一、首先,获取对本地文件读取的权限 在AndrooidManifest.xml中声明权限,以及页…

Windows,MySQL主从复制搭建

前提:windows环境,同一个服务器安装多个相同版本的mysql数据库 多个MySQL服务搭建完成后,下面我们进行主从复制的相关配置 1.主数据库 执行指令 #创建用户 CREATE USER slavelocalhost IDENTIFIED BY 123456;#授权 GRANT REPLICATION SLA…

[CS报错]找不到 .NETFramework,Version=v4.6.2 的引用程序集

报错 error MSB3644: 找不到 .NETFramework,Versionv4.6.2 的引用程序集。要解决此问题,请为此框架版本安装开发人员工具包(SDK/目标包)或者重新定向应用程序。可在 https://aka.ms/msbuild/developerpacks 处下载 .NET Framework 开发人员工具包 解决 打开https…

【计网】从零开始理解UDP协议 --- 理解端口号和UDP结构

我依旧敢和生活顶撞, 敢在逆境里撒野, 直面生活的污水, 永远乐意为新一轮的月亮和日落欢呼。 --- 央视文案 --- 从零开始理解UDP协议 1 再谈端口号2 理解UDP 报头结构3 UDP 的特点4 UDP 的缓冲区5 UDP 使用注意事项 1 再谈端口号 之前我…

pycharm里debug时如何看到数据的维度

使用表达式计算(Evaluate Expression) 调试时,使用 PyCharm 的 “Evaluate Expression” 功能可以动态查看或修改数据。具体步骤如下: 在调试模式中按 Alt F8(Windows)或 Option F8(Mac&…

Cortex-A7:如何切换ARM和Thumb状态

0 参考资料 ARM Cortex-A(armV7)编程手册V4.0.pdf1 Cortex-A7:如何切换ARM和Thumb状态 1.1 Cortex-A7支持的指令集 Cortex-A7支持的指令集包括ARM指令集和Thumb-2(ARM官方一般用Thumb表示)指令集。 ARM指令集指令大小都是32位,…

2024 年 04 月编程语言排行榜,PHP 排名创新低?

编程语言的流行度总是变化莫测,每个月的排行榜都揭示着新的趋势。2024年4月的编程语言排行榜揭示了一个引人关注的现象:PHP的排名再次下滑,创下了历史新低。这种变化对于PHP开发者和整个技术社区来说,意味着什么呢? P…

第十一章 RabbitMQ之消费者确认机制

目录 一、介绍 二、演示三种ACK方式效果 2.1. none: 不处理 2.1.1. 消费者配置代码 2.1.2. 生产者主要代码 2.1.3. 消费者主要代码 2.1.4. 运行效果 2.2. manual:手动模式 2.3. auto:自动模式 一、介绍 消费者确认机制(Consume…

【计算机网络】详谈TCP协议确认应答机制捎带应答机制超时重传机制连接管理机制流量管理机制滑动窗口拥塞控制延迟应答

一、TCP 协议段格式 1.1、4位首部长度 4位首部长度的基本单位是4字节,也就是说如果4位首部长度填6,那报头长度就是24字节。报头长度的取值范围为[0,60]字节,也就是说选项的最大长度为40字节。 二、确认应答机制 发送数据和发送应答&#x…

CSS 入门

1. CSS 1.1 概念 CSS(Cascading Style Sheet),层叠样式表,用于控制页面的样式 CSS 能够对网页中元素位置的排版进行像素级精确控制,实现美化页面的效果,能够做到页面的样式和结构分离(类似于…

Redis总结(官方文档解读)

定义 Redis是一个开源的,基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。 特征 高性能 支持丰富的数据类型 丰富的操作类型,操作是原子性的 支持周期性持久化 支持分布式 开源免费,社区活跃 数据类型 数据…