当前位置: 首页 > news >正文

使用RabbitMQ实现延迟消息的完整指南

在分布式系统中,消息队列通常用于解耦服务,RabbitMQ是一个广泛使用的消息队列服务。延迟消息(也称为延时队列或TTL消息)是一种常见的场景应用,特别适合处理某些任务在一段时间后执行的需求,如订单超时处理、延时通知等。

本文将以具体代码为例,展示如何使用RabbitMQ来实现延迟消息处理,涵盖队列和交换机的配置、消息的发送与接收以及死信队列的处理。

什么是延迟消息?

延迟消息是指消息在发送到队列后,经过设定的时间延迟再被消费。RabbitMQ 本身没有直接支持延迟队列的功能,但可以通过 TTL(Time To Live)+ 死信队列(Dead Letter Queue, DLQ) 的组合来实现。当消息超过TTL(消息存活时间)后,不会被立即消费,而是会被转发到绑定的死信队列,从而实现延迟处理。

RabbitMQ中的延迟消息原理

在RabbitMQ中,我们可以通过以下几个概念来实现延迟消息:

  1. TTL(Time To Live):可以为队列设置TTL,消息超过该时间后会被标记为“死信”。
  2. 死信队列(Dead Letter Queue):当消息在正常队列中过期或处理失败时,RabbitMQ可以将它们路由到一个死信队列,死信队列可以用来处理这些过期或未处理的消息。
  3. x-dead-letter-exchangex-dead-letter-routing-key:可以通过配置队列的参数,将过期消息发送到一个专门的死信交换器,并根据指定的路由键转发到死信队列。

 

 消息来到ttl.queue消息队列,过期时间内无人消费,消息来到死信交换机hmall.direct,在direct.queue消息队列无需等待。

1. RabbitMQ的配置

首先,我们需要配置两个队列和两个交换机:一个用于存放延时消息,另一个用于处理超时的死信消息。

package com.heima.stroke.configuration;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;@Configuration
public class RabbitConfig {// 延迟时间 单位:毫秒 (这里设为30秒)private static final long DELAY_TIME = 1000 * 30;// 行程超时队列public static final String STROKE_OVER_QUEUE = "STROKE_OVER_QUEUE";// 行程死信队列public static final String STROKE_DEAD_QUEUE = "STROKE_DEAD_QUEUE";// 行程超时队列交换机public static final String STROKE_OVER_QUEUE_EXCHANGE = "STROKE_OVER_QUEUE_EXCHANGE";// 行程死信队列交换机public static final String STROKE_DEAD_QUEUE_EXCHANGE = "STROKE_DEAD_QUEUE_EXCHANGE";// 行程超时交换机 Routing Keypublic static final String STROKE_OVER_KEY = "STROKE_OVER_KEY";// 行程死信交换机 Routing Keypublic static final String STROKE_DEAD_KEY = "STROKE_DEAD_KEY";/*** 声明行程超时队列,并设置其参数* x-dead-letter-exchange:绑定的死信交换机* x-dead-letter-routing-key:死信路由Key* x-message-ttl:消息的过期时间*/@Beanpublic Queue strokeOverQueue() {Map<String, Object> args = new HashMap<>(3);args.put("x-dead-letter-exchange", STROKE_DEAD_QUEUE_EXCHANGE);args.put("x-dead-letter-routing-key", STROKE_DEAD_KEY);args.put("x-message-ttl", DELAY_TIME); // 设置TTL为30秒return QueueBuilder.durable(STROKE_OVER_QUEUE).withArguments(args).build();}@Beanpublic DirectExchange strokeOverQueueExchange() {return new DirectExchange(STROKE_OVER_QUEUE_EXCHANGE);}@Beanpublic Binding bindingStrokeOverDirect() {return BindingBuilder.bind(strokeOverQueue()).to(strokeOverQueueExchange()).with(STROKE_OVER_KEY);}
}

解释:

TTL设置:我们通过x-message-ttl设置消息的过期时间为30秒。

死信队列绑定:通过x-dead-letter-exchangex-dead-letter-routing-key设置,当消息过期时,它会被转发到死信交换机,再路由到死信队列。

2. 生产者发送延迟消息

接下来,我们通过生产者向超时队列发送消息,这些消息将在TTL过期后转发到死信队列。

package com.heima.stroke.rabbitmq;import com.alibaba.fastjson.JSON;
import com.heima.modules.vo.StrokeVO;
import com.heima.stroke.configuration.RabbitConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MQProducer {private final static Logger logger = LoggerFactory.getLogger(MQProducer.class);@AutowiredRabbitTemplate rabbitTemplate;/*** 发送延时消息到行程超时队列** @param strokeVO 消息体*/public void sendOver(StrokeVO strokeVO) {String mqMessage = JSON.toJSONString(strokeVO);logger.info("send timeout msg:{}", mqMessage);rabbitTemplate.convertAndSend(RabbitConfig.STROKE_OVER_QUEUE_EXCHANGE, RabbitConfig.STROKE_OVER_KEY, mqMessage);}
}

解释:

sendOver 方法将消息发送到超时队列,消息将在超时后进入死信队列。生产者不需要额外处理TTL或死信的配置,只需发送消息即可。

3. 消费者监听死信队列

当消息超过TTL后,将会被转发到死信队列。消费者需要监听死信队列并处理这些消息。

j

package com.heima.stroke.rabbitmq;import com.alibaba.fastjson.JSON;
import com.heima.modules.vo.StrokeVO;
import com.heima.stroke.configuration.RabbitConfig;
import com.heima.stroke.handler.StrokeHandler;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;@Component
public class MQConsumer {private final static Logger logger = LoggerFactory.getLogger(MQConsumer.class);@Autowiredprivate StrokeHandler strokeHandler;/*** 监听死信队列** @param message 消息体* @param channel RabbitMQ的Channel* @param tag 消息的Delivery Tag*/@RabbitListener(bindings = {@QueueBinding(value = @Queue(value = RabbitConfig.STROKE_DEAD_QUEUE, durable = "true"),exchange = @Exchange(value = RabbitConfig.STROKE_DEAD_QUEUE_EXCHANGE),key = RabbitConfig.STROKE_DEAD_KEY)})@RabbitHandlerpublic void processStroke(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {StrokeVO strokeVO = JSON.parseObject(message.getBody(), StrokeVO.class);logger.info("get dead msg:{}", message.getBody());if (strokeVO == null) {return;}try {// 处理超时的行程消息strokeHandler.timeoutHandel(strokeVO);// 手动确认消息channel.basicAck(tag, false);} catch (Exception e) {e.printStackTrace();}}
}

解释:

@RabbitListener 注解绑定了死信队列的监听器。当消息被转发到死信队列时,该消费者会接收到消息。

使用 channel.basicAck(tag, false) 手动确认消息处理成功,确保消息不会重复消费。

4. 处理超时业务逻辑

在我们的业务中,当消息超时未处理时,将其状态设置为超时。

public void timeoutHandel(StrokeVO strokeVO) {// 获取司机行程ID和乘客行程IDString inviterTripId = strokeVO.getInviterTripId();String inviteeTripId = strokeVO.getInviteeTripId();// 检查邀请状态是否为未确认String inviteeStatus = redisHelper.getHash(HtichConstants.STROKE_INVITE_PREFIX, inviteeTripId, inviterTripId);String inviterStatus = redisHelper.getHash(HtichConstants.STROKE_INVITE_PREFIX, inviterTripId, inviteeTripId);if (String.valueOf(InviteState.UNCONFIRMED.getCode()).equals(inviteeStatus) &&String.valueOf(InviteState.UNCONFIRMED.getCode()).equals(inviterStatus)) {// 更新为超时状态redisHelper.addHash(HtichConstants.STROKE_INVITE_PREFIX, inviteeTripId, inviterTripId, String.valueOf(InviteState.TIMEOUT.getCode()));redisHelper.addHash(HtichConstants.STROKE_INVITE_PREFIX, inviterTripId, inviteeTripId, String.valueOf(InviteState.TIMEOUT.getCode()));}
}

相关文章:

使用RabbitMQ实现延迟消息的完整指南

在分布式系统中&#xff0c;消息队列通常用于解耦服务&#xff0c;RabbitMQ是一个广泛使用的消息队列服务。延迟消息&#xff08;也称为延时队列或TTL消息&#xff09;是一种常见的场景应用&#xff0c;特别适合处理某些任务在一段时间后执行的需求&#xff0c;如订单超时处理、…...

阿里员工:阿里工作7年至少得P7吧,快的都P8了,年薪100W是正常的,80才算及格...

上一篇&#xff1a;一线体面男的收入 年薪64W的阿里蚂蚁员工爆料&#xff1a;在阿里&#xff0c;工作7年至少得P7&#xff0c;快的都P8了&#xff0c;年薪100W才正常&#xff0c;80分才算及格。 其实&#xff0c;在大厂工作&#xff0c;听起来风光无限&#xff0c;但个中滋味&a…...

Django进一步掌握(10月22日)

一、请求响应对象 请求对象request 响应对象HttpResponse 二、HttpResponse常用属性 status设置HTTP响应状态码 status_code查询HTTP响应状态码 content_type设置响应的类型 write()写入响应内容 三、重定向 1、实现URl访问的重定向 &#xff08;1&#xff09;使用Ht…...

C++从入门到起飞之——红黑树封装map和set 全方位剖析!

目录 1、map和set的整体框架 2、map和set迭代器的实现 3、map支持[] 4、完整源码 set.h map.h RBTree.h 1、map和set的整体框架 因为map和set的底层都是红黑树&#xff0c;所以我们考虑用一个红黑树的类模版去实例化map和set对象&#xff01;不过&#xff0c;map节点中存…...

【javax maven项目缺少_Maven的依赖管理 引入依赖】

javax maven项目缺少_Maven的依赖管理 引入依赖 Maven的依赖管理 - 引入依赖依赖管理(引入依赖)导入依赖 https://blog.csdn.net/weixin_28932089/article/details/112381468 Maven的依赖管理 - 引入依赖 依赖管理(引入依赖) 能够掌握依赖引入的配置方式 导入依赖 导入依赖练…...

手搓一个定时器

目录 1.什么是定时器 2.计时器的使用 3.手搓定时器 3.1定义一个TimerTask类 3.2定义一个Timer类 3.3实现schedule方法 3.4实现Timer的构造方法 3.4.1随时随地查看优先级队列中是否有任务要执行 3.4.2获取队首任务&#xff0c;并判断是否到执行时间 3.4.3到达执行时间…...

AI提示词工程优化Prompt-GPT使用手册(科普一键收藏史上最强攻略)

Prompt(提示)&#xff0c;最初是 NLP 研究者为下游任务设计出来的一种任务专属的输入形式或模板。在 ChatGPT 引发大语言模型新时代之后&#xff0c;Prompt 指与大模型交互输入的代称。 随着大模型的进展&#xff0c;Prompt Engineering是一个持久的探索过程。 目录 什么是提示…...

【数据结构】快速排序(三种实现方式)

目录 一、基本思想 二、动图演示&#xff08;hoare版&#xff09; 三、思路分析&#xff08;图文&#xff09; 四、代码实现&#xff08;hoare版&#xff09; 五、易错提醒 六、相遇场景分析 6.1 ❥ 相遇位置一定比key要小的原因 6.2 ❥ 右边为key&#xff0c;左边先走 …...

利用前向勾子获取神经网络中间层的输出并将其进行保存(示例详解)

代码示例&#xff1a; # 激活字典&#xff0c;用于保存每次的中间特征 activation {}# 将 forward_hook 函数定义在 upsample_v2 外部 def forward_hook(name):def hook(module, input, output):activation[name] output.detach()return hookdef upsample_v2(in_channels, o…...

CTF-RE 从0到N: S盒

S盒&#xff08;Substitution Box&#xff09; 是密码学中的一种替换表&#xff0c;用于对输入数据进行非线性变换&#xff0c;以增加加密过程的复杂性。它主要用于对称加密算法中&#xff08;例如AES、DES&#xff09;&#xff0c;作为加密轮次的一部分&#xff0c;对输入字节…...

MT-Pref数据集:包含18种语言的18k实例,涵盖多个领域。实验表明它能有效提升Tower模型在WMT23和FLORES基准测试中的翻译质量。

2024-10-10&#xff0c;由电信研究所、里斯本大学等联合创建MT-Pref数据集&#xff0c;它包含18种语言方向的18k实例&#xff0c;覆盖了2022年后的多个领域文本。通过在WMT23和FLORES基准测试上的实验&#xff0c;我们展示了使用MT-Pref数据集对Tower模型进行对齐可以显著提高翻…...

【C++ 真题】B2099 矩阵交换行

矩阵交换行 题目描述 给定一个 5 5 5 \times 5 55 的矩阵(数学上&#xff0c;一个 r c r \times c rc 的矩阵是一个由 r r r 行 c c c 列元素排列成的矩形阵列)&#xff0c;将第 n n n 行和第 m m m 行交换&#xff0c;输出交换后的结果。 输入格式 输入共 6 6 6 …...

AAPL: Adding Attributes to Prompt Learning for Vision-Language Models

文章汇总 当前的问题 1.元标记未能捕获分类的关键语义特征 如下图(a)所示&#xff0c; π \pi π在类聚类方面没有显示出很大的差异&#xff0c;这表明元标记 π \pi π未能捕获分类的关键语义特征。我们进行简单的数据增强后&#xff0c;如图(b)所示&#xff0c;效果也是如…...

MySQLDBA修炼之道-开发篇(一)

三、开发基础 1. 数据模型 1.1 关系数据模型介绍 关于NULL 如果某个字段的值是未知的或未定义的&#xff0c;数据库会提供一个特殊的值NULL来表示。NULL值很特殊&#xff0c;在关系数据库中应该小心处理。例如查询语句“select*from employee where 绩效得分<85 or>绩…...

Spring MVC 知识点全解析

Spring MVC 知识点全解析 Spring MVC 是一个基于 Java 的请求驱动的 Web 框架&#xff0c;属于 Spring 框架的一部分&#xff0c;广泛用于构建企业级 Web 应用程序。本文将详细阐述 Spring MVC 的核心知识点&#xff0c;包括其工作原理、关键组件、配置、请求处理、数据绑定、…...

python 基于FastAPI实现一个简易的在线用户统计 服务

简易在线用户统计服务 概述 这是一个基于Python的FastAPI框架实现的服务&#xff0c;用于统计客户端的心跳信息&#xff0c;并据此维护在线用户列表以及记录活跃用户数。 功能特性 心跳接收&#xff1a;接受来自客户端的心跳包&#xff0c;以更新客户端的状态。在线用户统计…...

glibc中xdr的一个bug

本人在64位linux服务器上(centos7)&#xff0c;发现xdr_u_long这个函数有个bug&#xff0c;就是数字的范围如果超过unsigned int的最大值(4294967295)时&#xff0c;xdr_u_long失败。 这个场景主要用在unix时间戳上面&#xff0c;比如一款软件&#xff0c;设置有效期为100年。…...

Android Framework定制sim卡插入解锁pin码的界面

文章目录 手机设置SIM卡pin码一、安卓手机二、苹果手机 Android Framework中SIM卡pin码代码定位pin码提示文本位置定位pin码java代码位置 定制pin码framework窗口数字按钮 手机设置SIM卡pin码 设置 SIM 卡 PIN 码可以提高手机的安全性&#xff0c;防止他人在未经授权的情况下使…...

cc2530 Basic RF 讲解 和点灯讲解(1_1)

1. Basic RF 概述 Basic RF 是 TI 提供的一套简化版的无线通信协议栈&#xff0c;旨在帮助开发者快速搭建无线通信系统。它基于 IEEE 802.15.4 标准的数据包收发&#xff0c;但只用于演示无线设备数据传输的基本方法&#xff0c;不包含完整功能的协议。Basic RF 的功能限制包括…...

Android H5页面性能分析策略

文章目录 引言一、拦截资源加载请求以优化性能二、通过JavaScript代码监控资源下载速度三、使用vConsole进行前端性能调试四、使用Chrome DevTools调试Android端五、通过抓包分析优化网络性能六、总结 引言 在移动应用开发中&#xff0c;H5页面的性能直接影响到用户体验。本文…...

cv_resnet101_face-detection_cvpr22papermogface 模型压缩与加速:面向边缘设备的部署探索

cv_resnet101_face-detection_cvpr22papermogface 模型压缩与加速&#xff1a;面向边缘设备的部署探索 1. 引言 想象一下&#xff0c;你手里有一台小巧的Jetson Nano开发板&#xff0c;想让它实时识别人脸&#xff0c;完成门禁或者客流统计。你兴冲冲地找到了一个效果不错的模…...

【2025最新】基于SpringBoot+Vue的小型企业客户关系管理系统管理系统源码+MyBatis+MySQL

摘要 在当今竞争激烈的商业环境中&#xff0c;小型企业亟需高效的客户关系管理&#xff08;CRM&#xff09;系统来优化客户交互、提升销售效率并增强客户忠诚度。传统的客户管理方式依赖人工记录和电子表格&#xff0c;存在数据冗余、查询效率低、信息共享困难等问题。随着信息…...

YOLOv8实战:TaskAlignedAssigner在目标检测中的动态样本匹配技巧

YOLOv8实战&#xff1a;TaskAlignedAssigner在目标检测中的动态样本匹配技巧 目标检测作为计算机视觉领域的核心任务之一&#xff0c;其性能提升的关键在于如何更有效地匹配预测框与真实标注。YOLOv8引入的TaskAlignedAssigner通过创新的动态匹配策略&#xff0c;显著提升了检测…...

电子电路耦合技术详解与应用指南

1. 电子电路中的耦合技术解析1.1 耦合的基本概念在电子电路设计中&#xff0c;耦合是指将前级电路&#xff08;信号源&#xff09;的能量传递至后级电路&#xff08;负载&#xff09;的技术过程。这一基础概念在各类电子系统中具有普遍应用价值&#xff0c;特别是在多级放大电路…...

PP-DocLayoutV3保姆级教程:从部署到API调用,手把手教你分析文档结构

PP-DocLayoutV3保姆级教程&#xff1a;从部署到API调用&#xff0c;手把手教你分析文档结构 1. 认识PP-DocLayoutV3&#xff1a;文档分析的智能助手 1.1 模型能做什么 想象一下&#xff0c;当你拿到一份扫描的合同或论文时&#xff0c;需要快速找到其中的关键部分——标题、…...

Linux系统下Materials Studio 2020安装全攻略:从依赖安装到许可证配置

Linux系统下Materials Studio 2020专业安装与优化指南 1. 环境准备与系统配置 在开始安装Materials Studio 2020之前&#xff0c;确保您的Linux系统满足以下要求&#xff1a; 操作系统&#xff1a;推荐使用CentOS 7.x或8.x、Red Hat Enterprise Linux 7/8、Ubuntu 18.04/20.04 …...

一秒预警・守护平安|腾视科技DS02 DMS疲劳驾驶预警仪,AI守护每一段旅途

行车千万里&#xff0c;安全第一位。在长途货运、网约车、公交、特种车辆、船舶驾驶及各类值守岗位&#xff0c;疲劳驾驶、分心驾驶已成为安全事故的主要诱因。腾视科技依托AI算力与视觉算法核心优势&#xff0c;重磅推出DS02 DMS疲劳驾驶预警仪&#xff0c;以高精度AI算法、智…...

避坑指南:Unity环境搭建中最容易忽略的5个配置项(含Android/iOS模块选择建议)

Unity环境搭建避坑指南&#xff1a;5个高频遗漏配置与移动端优化策略 刚接触Unity的开发者往往会被其强大的跨平台能力吸引&#xff0c;却在环境搭建阶段就踩进各种"坑"。我曾见过团队因一个遗漏的配置项浪费三天排查时间&#xff0c;也遇到过新手因模块选择不当导致…...

cvpr论文学习《Generative Image Dynamics》

2024年cvpr最佳论文https://arxiv.org/pdf/2309.07906 CVPR 2024的最佳论文《Generative Image Dynamics》提出了一种从单张静态图像生成逼真、连续运动视频的技术&#xff0c;其核心在于将运动建模在频域&#xff0c;并通过扩散模型进行预测。 下面这个流程图概括了它的核心工…...

gh_mirrors/dnf/dnf容器化部署与多环境适配技术指南

gh_mirrors/dnf/dnf容器化部署与多环境适配技术指南 【免费下载链接】dnf 项目地址: https://gitcode.com/gh_mirrors/dnf/dnf gh_mirrors/dnf/dnf项目通过Docker容器化技术实现了地下城与勇士(DNF)服务端的跨平台部署&#xff0c;支持在Linux服务器、群晖NAS等多种环境…...