第十六章 RabbitMQ延迟消息之延迟插件优化

目录

一、引言

二、优化方案 

三、核心代码实现

3.1. 生产者代码

3.2. 消息处理器 

3.3. 自定义多延迟消息封装类 

3.4. 订单实体类 

3.5. 消费者代码 

四、运行效果


一、引言

上一章节我们提到,直接使用延迟插件,创建一个延迟指定时间的消息(如10分钟),并不是最好的解决方案,因为假如我们的订单是在5分钟支付的,那么剩余的5分钟时间,RabbitMQ中延迟消息时钟还是一直占用着资源。如果有大量的延迟消息,那么对于服务来说压力是很大的,同时会耗费庞大昂贵的资源。因此,本章节我们就来近一步对延迟插件的消息进行优化。

我们通过下面的流程图来做近一步分析:

1. 用户下单完成后,发送15分钟延迟消息,在15分钟后接收消息,检查支付状态:

2. 已支付:更新订单状态为已支付

3. 未支付:更新订单状态为关闭订单,恢复商品库存

常规延迟插件消息使用的弊端总结:

1. 设置30分钟后检测订单支付状态实现起来非常简单,但是存在两个问题:

2. 如果并发较高,30分钟可能堆积消息过多,对MQ压力很大

3. 大多数订单在下单后1分钟内就会支付,但是却需要在MQ内等待30分钟,浪费资源

二、优化方案 

如下图所示,我们可以将10分钟甚至30分钟拆分成多份零散的较短的时间。

消息初次发送的延迟时间设定为10s,10s过后如果订单还是未支付状态,我们判断延迟时间数组里还有没有剩余延迟时间,如果有则继续发送延迟消息,时间设定为数组中的第二个时间10s,直到订单支付成功终止循环,或是最后一份时间消耗完依然未支付,我们取消订单。

三、核心代码实现

3.1. 生产者代码

package com.example.publisher;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;/*** 生产者*/
@Slf4j
@SpringBootTest
class PublisherApplicationTests {@Resourceprivate RabbitTemplate rabbitTemplate;@Testvoid test() {Order order = Order.builder().orderId(1L).content("生活不易,所以保持足够的努力,对自己要有信心,积极地去面对工作生活的挑战!").build();MultiDelayMessage<Order> msg = MultiDelayMessage.of(order, 1000L, 5000L, 2000L, 10000L);rabbitTemplate.convertAndSend("delay.direct", "delay", msg, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setDelayLong(msg.removeNextDelay());return message;}});}
}

3.2. 消息处理器 

package com.example.publisher;import lombok.AllArgsConstructor;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;/*** 消息请求处理器*/
@AllArgsConstructor
public class DelayMessageProcessor implements MessagePostProcessor {private final Long delay;@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setDelayLong(delay);return message;}
}

3.3. 自定义多延迟消息封装类 

package com.example.publisher;import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;import java.io.Serializable;
import java.util.List;/*** 自定义的多延时消息封装类* @param <T>*/
@Data
@NoArgsConstructor
public class MultiDelayMessage<T> implements Serializable {/*** 消息体*/private T data;/*** 记录延迟时间的集合*/private List<Long> delayMillis;public MultiDelayMessage(T data, List<Long> delayMillis) {this.data = data;this.delayMillis = delayMillis;}public static <T> MultiDelayMessage<T> of(T data, Long...delayMillis) {return new MultiDelayMessage<>(data, (List<Long>) CollectionUtils.arrayToList(delayMillis));}/*** 获取并移除下一个延迟时间* @return 队列中的第一个延迟时间*/public Long removeNextDelay() {return delayMillis.remove(0);}/*** 是否还有下一个延迟时间* @return*/public boolean hasNextDelay() {return !delayMillis.isEmpty();}
}

3.4. 订单实体类 

package com.example.publisher;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 订单类 * 此处为了演示,将真实业务中的订单类做了简化* 只包含一个订单ID和自定义消息内容*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order {private Long orderId;private String content;
}

3.5. 消费者代码 

package com.example.consumer;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;/*** 消费者* 因为作为演示,所以商城支付、订单、及扣减库存的业务代码已注释* 注释中保留了整个商城下单支付扣减库存的流程步骤*/
@Slf4j
@Component
public class SimpleListener {@Resourceprivate RabbitTemplate rabbitTemplate;@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "delay.queue", durable = "true"),exchange = @Exchange(name = "delay.direct", delayed = "true"),key = "delay"))public void listener(MultiDelayMessage<Order> msg) throws Exception {System.out.println(((Order)msg.getData()).getContent());// 1. 查询订单状态// Order order = orderService.getById(msg.getData())// 2. 判断是否已支付
//        if (Order == null || order.status == 2) {
//            订单不存在或者已处理则直接返回
//            return;
//        }// 主动去支付服务查询真正的支付状态
//        PayOrder payOrder = payService.getById(order.getId());// 2.1. 已支付,则标记订单为已支付
//        if (payOrder.isPay()) {
//            orderService.markOrderPaySuccess(order.getId());
//            return;
//        }// 2.2. 未支付,获取下次订单延迟时间// 3. 判断是否存在延迟时间if (msg.hasNextDelay()) {// 3.1 存在,重发延迟消息Long nextDelay = msg.removeNextDelay();rabbitTemplate.convertAndSend("delay.direct", "delay", msg, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setDelayLong(nextDelay);return message;}});return;}// 3.2 不存在,取消订单
//        orderService.lambdaUpdate()
//                .set(Order::getStatus, 5);
//                .set(Order::getCloseTime, LocalDateTime.now());
//                .eq(Order::getId, order.getId())
//                .update();// 4. 恢复库存}
}

四、运行效果

最终我们会看到每间隔一段时间消费者就会消费一条消息,这个间隔时间就是我们设定的分段时间数组,这么做就能极大地减少资源消耗和服务的压力:

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

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

相关文章

晶体匹配测试介绍

一、晶体参数介绍 晶体的电气规格相对比较简单,如下: 我们逐一看看每个参数, FL就是晶体的振动频率,这个晶体是24.576MHz的。 CL就是负载电容,决定了晶体频率是否准确,包括外接的实际电容、芯片的等效电容以及PCB走线的寄生电容等,核心参数。 Frequency Tolerance是…

堆排序(C++实现)

参考&#xff1a; 面试官&#xff1a;请写一个堆排序_哔哩哔哩_bilibiliC实现排序算法_c从小到大排序-CSDN博客 堆的基本概念 堆排实际上是利用堆的性质来进行排序。堆可以看做一颗完全二叉树。 堆分为两类&#xff1a; 最大堆&#xff08;大顶堆&#xff09;&#xff1a;除根…

Deep tone mapping network in HSV color space

Abstract 色调映射算子可以将高动态范围(HDR)图像转换为低动态范围(LDR)图像&#xff0c;这样我们就可以用LDR设备享受HDR图像的信息内容。然而&#xff0c;目前的色调映射算法主要关注亮度映射&#xff0c;而忽略了颜色分量。与此同时&#xff0c;它们经常遭受光晕伪影和过度…

IaaS,PaaS和SaaS的区别讲解

IaaS、PaaS和SaaS有什么区别吗&#xff1f;这三个概念非常简单。 只不过在说它们仨的区别前&#xff0c;有个常识需要知道一下&#xff1a; 我们传统开发一个软件&#xff0c;需要9个东西&#xff1a; 作为使用软件的人&#xff0c;左边的【应用】和【数据】&#xff0c;是离…

Django的请求与响应

Django的请求与响应 1、常见的请求2、常见的响应3、案例 1、常见的请求 函数的参数request是一个对象&#xff0c;封装了用户发送过来的所有请求相关数据。 get请求一般用来请求获取数据&#xff0c;get请求也可以传参到后台&#xff0c;但是传递的参数显示在地址栏。 post请求…

企业内部文档安全外发如何挑选合适的外发系统?

企业文档的外发不仅关系到运营效率&#xff0c;更是信息安全的重要组成部分。面对B2B模式下文档交换的普遍性和重要性&#xff0c;企业内部文档的安全外发成为了众多公司关注的重点之一。 随着互联网技术的发展&#xff0c;企业之间的合作越来越紧密&#xff0c;文档的交流也变…

Java Agent 技术解析

什么是Java Agent Java Agent是在 JDK1.5 引入的一种可以动态修改 Java 字节码的技术。Java 类编译之后形成字节码被 JVM 执行&#xff0c;在 JVM 在执行这些字节码之前获取这些字节码信息&#xff0c;并且通过字节码转换器对这些字节码进行修改&#xff0c;来完成一些额外的功…

第十四章:收尾过程组(14.1结束项目或阶段--14.2收尾过程组重点工作)

14.1 结束项目或阶段 过程定义&#xff1a;终结项目、阶段或合同的所有活动的过程 14.1.1 主要输入 1.项自章程 项目章程记录了项目成功标准、审批要求&#xff0c;以及由谁来签署项目结束 2.项目管理计划 项目管理计划的所有组成部分均为结束项目或阶段过程的输入。 3.项…

【视觉分割新SOTA|论文解读1】一种最先进的图像分割模型——Segment Anything Model (SAM)

【视觉分割新SOTA|论文解读1】一种最先进的图像分割模型——Segment Anything Model (SAM) 【视觉分割新SOTA|论文解读1】一种最先进的图像分割模型——Segment Anything Model (SAM) 文章目录 【视觉分割新SOTA|论文解读1】一种最先进的图像分割模型——Segment Anything Mod…

全院级、流程化的医院安全不良事件管理系统源码——等级医院评审工作的辅助工具

前言&#xff1a; 冰山理论”指出“每件严重不良事件背后可能隐藏着10件轻微的不良事件”“存在30件未造成伤害的差错可能存在600件引发意外的异常事件”没有一件不良事件应该被忽视&#xff01; 一项研究也指出95%医生曾目睹错误的发生&#xff0c;61%的医务人员认为医疗错误…

基于Python星载气溶胶数据处理与反演分析技术

MODIS&#xff08;中分辨率成像光谱仪&#xff09;和CALIOP&#xff08;云-气溶胶偏振激光雷达&#xff09;是两种重要的星载遥感观测平台&#xff0c;它们提供了大量的气溶胶数据。MODIS通过成像光谱技术获取不同波长的遥感数据&#xff0c;从而得到气溶胶的空间分布、光学厚度…

耳夹式耳机哪个最好?2024年五大热门耳夹式耳机品牌分享

耳夹式耳机哪个最好&#xff1f;2024年五大热门耳夹式耳机品牌分享 耳夹式蓝牙耳机怎样才算好、算优质呢&#xff1f;哪款比较好呢&#xff1f;对于第一个问题&#xff0c;我认为耳夹式蓝牙耳机得具备以下几个特征优势才称得上是优质产品。其一&#xff0c;要能提供清晰、平衡…

nuxtjs使用rem 实现自适应窗口的大小

效果图&#xff1a; 步骤 1&#xff1a;安装 PostCSS 和 PostCSS 插件 npm install postcss postcss-pxtorem --save-dev步骤 2&#xff1a;配置 nuxt.config.ts // nuxt.config.ts export default defineNuxtConfig({compatibilityDate: 2024-04-03,devtools: { enabled: …

本地windows文件上传到远程阿里云windows server方法

一.功能简介 在本地windows下开发完成软件后&#xff0c;需要上传到远程阿里云服务器进行发布&#xff0c;可使用该方法&#xff0c;快速实现本地文件上传。 二.方法 在本地windows系统使用快捷键 winR&#xff0c;打开运行对话框&#xff0c;‌通过这个对话框&#xff0c;用…

解决Windows Server 2016本地登录失败但远程登录正常的问题:排查与解决方案

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

2024VDC蓝河分会场:蓝河操作系统2 全栈自研 为AI而生

10月10日&#xff0c; 以“同心同行”为主题的2024vivo开发者大会在深圳成功举办&#xff0c;在同期举办的蓝河分会场上&#xff0c;vivo多位专家及产业界、学术界伙伴分享了在AGI时代下&#xff0c;蓝河操作系统带来的技术创新与实践&#xff0c;vivo希望携各方共建生态&#…

Monad 101 杭州线下活动:解锁创新技术,引领低成本高效 DApp 开发之路!

以太坊等区块链在处理传统金融大规模交易时面临巨大挑战&#xff0c;有限的可扩展性成为阻碍其广泛应用的主要瓶颈。为了解决这一难题&#xff0c;并缩小传统金融与去中心化金融&#xff08;DeFi&#xff09;之间的差距&#xff0c;Keone 创立了 Monad。通过显著提升交易速度和…

能效电气发布“四全”欧标直流桩系列产品

2024年10月12日,深圳 分布式充放电全球第一品牌、新型充放电解决方案卓越供应商,电动汽车充放电行业颠覆者、创新者、标准制定者、市场领导者,深圳市能效电气技术有限公司发布面向全球市场的全系列欧标直流充电桩产品,功率范围覆盖22kW-160kW,包括8大系列12种型号:20kW UE20、2…

2024年最新Stable Diffusion模型资源合集!附整合安装包!

&#xff08;模型资源在ComfyUI、WebUI以及ForgeUI中都通用&#xff09; 之前的Stable Diffusion笔记受到了不少小伙伴的关注&#xff0c;很感谢大家的建议和支持。有很多小伙伴私信我问我一些AI绘画的模型资源在哪来下载&#xff0c;一般来说有两个网站比较常用&#xff0c;分…

软件测试学习笔记丨Linux三剑客-grep

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32506 一、简介 1.1 grep命令 grep是一个全局查找正则表达式&#xff0c;并且打印结果行的命令。grep的输入是一个文件或者一个标准输入&#xff08;stdin&#xff09;&#xff0c;或者是一…