延迟队列实现,定时任务,关闭订单

RabbitMQ:@RabbitListener注解简化消息监听 pom.xml: <?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/ma 阅读详情

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 8:55 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:https://blog.youkuaiyun.com/wantnrun/article/details/80401641

  • Rabbitmq延迟队列

  • 创建交换机(Exchanges)和队列(Queues)

  • 消息队列绑定到交换机

  • 发送消息

  • 接收消息

  • 总结

场景

开发中经常需要用到定时任务,对于商城来说,定时任务尤其多,比如优惠券定时过期、订单定时关闭、微信支付2小时未支付关闭订单等等,都需要用到定时任务,但是定时任务本身有一个问题,一般来说我们都是通过定时轮询查询数据库来判断是否有任务需要执行,也就是说不管怎么样,我们需要先查询数据库,而且有些任务对时间准确要求比较高的,需要每秒查询一次,对于系统小倒是无所谓,如果系统本身就大而且数据也多的情况下,这就不大现实了,所以需要其他方式的,当然实现的方式有多种多样的,比如Redis实现定时队列、基于优先级队列的JDK延迟队列、时间轮等。因为我们项目中本身就使用到了Rabbitmq,所以基于方便开发和维护的原则,我们使用了Rabbitmq延迟队列来实现定时任务,不知道rabbitmq是什么的和不知道springboot怎么集成Rabbitmq的可以查看我之前的文章Spring boot集成RabbitMQ

Rabbitmq延迟队列

Rabbitmq本身是没有延迟队列的,只能通过Rabbitmq本身队列的特性来实现,想要Rabbitmq实现延迟队列,需要使用Rabbitmq的死信交换机(Exchange)和消息的存活时间TTL(Time To Live)

死信交换机

一个消息在满足如下条件下,会进死信交换机,记住这里是交换机而不是队列,一个交换机可以对应很多队列。

一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。

上面的消息的TTL到了,消息过期了。

队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。

死信交换机就是普通的交换机,只是因为我们把过期的消息扔进去,所以叫死信交换机,并不是说死信交换机是某种特定的交换机

消息TTL(消息存活时间)

消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不一样(不同的队列设置)。这里单讲单个消息的TTL,因为它才是实现延迟任务的关键。

byte[] messageBodyBytes = "Hello, world!".getBytes();
AMQP.BasicProperties properties = new AMQP.BasicProperties();
properties.setExpiration("60000");
channel.basicPublish("my-exchange", "queue-key", properties, messageBodyBytes);

可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是一样的效果。只是expiration字段是字符串参数,所以要写个int类型的字符串:当上面的消息扔到队列中后,过了60秒,如果没有被消费,它就死了。不会被消费者消费到。这个消息后面的,没有“死掉”的消息对顶上来,被消费者消费。死信在队列中并不会被删除和释放,它会被统计到队列的消息数中去

处理流程图

创建交换机(Exchanges)和队列(Queues)

创建死信交换机

如图所示,就是创建一个普通的交换机,这里为了方便区分,把交换机的名字取为:delay

创建自动过期消息队列

这个队列的主要作用是让消息定时过期的,比如我们需要2小时候关闭订单,我们就需要把消息放进这个队列里面,把消息过期时间设置为2小时创建一个一个名为delay_queue1的自动过期的队列,当然图片上面的参数并不会让消息自动过期,因为我们并没有设置x-message-ttl参数,如果整个队列的消息有消息都是相同的,可以设置,这里为了灵活,所以并没有设置,另外两个参数x-dead-letter-exchange代表消息过期后,消息要进入的交换机,这里配置的是delay,也就是死信交换机,x-dead-letter-routing-key是配置消息过期后,进入死信交换机的routing-key,跟发送消息的routing-key一个道理,根据这个key将消息放入不同的队列

创建消息处理队列

这个队列才是真正处理消息的队列,所有进入这个队列的消息都会被处理消息队列的名字为delay_queue2

消息队列绑定到交换机

进入交换机详情页面,将创建的2个队列(delayqueue1和delayqueue2)绑定到交换机上面自动过期消息队列的routing key 设置为delay

绑定delayqueue2delayqueue2 的key要设置为创建自动过期的队列的x-dead-letter-routing-key参数,这样当消息过期的时候就可以自动把消息放入delay_queue2这个队列中了

绑定后的管理页面如下图:

当然这个绑定也可以使用代码来实现,只是为了直观表现,所以本文使用的管理平台来操作

发送消息

String msg = "hello word";  MessageProperties messageProperties = newMessageProperties();          messageProperties.setExpiration("6000");        messageProperties.setCorrelationId(UUID.randomUUID().toString().getBytes());        Message message = newMessage(msg.getBytes(), messageProperties);        rabbitTemplate.convertAndSend("delay", "delay",message);

主要的代码就是

messageProperties.setExpiration("6000");

设置了让消息6秒后过期

注意:因为要让消息自动过期,所以一定不能设置delay_queue1的监听,不能让这个队列里面的消息被接受到,否则消息一旦被消费,就不存在过期了

接收消息

接收消息配置好delay_queue2的监听就好了

package wang.raye.rabbitmq.demo1;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DelayQueue {
 /** 消息交换机的名字*/
 public static final String EXCHANGE = "delay";
 /** 队列key1*/
 public static final String ROUTINGKEY1 = "delay";
 /** 队列key2*/
 public static final String ROUTINGKEY2 = "delay_key";

 /**
  * 配置链接信息
  * @return
  */
 @Bean
 public ConnectionFactory connectionFactory() {
  CachingConnectionFactory connectionFactory = new CachingConnectionFactory("120.76.237.8",5672);

  connectionFactory.setUsername("kberp");
  connectionFactory.setPassword("kberp");
  connectionFactory.setVirtualHost("/");
  connectionFactory.setPublisherConfirms(true); // 必须要设置
  return connectionFactory;
 }

 /**
  * 配置消息交换机
     * 针对消费者配置
        FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
        HeadersExchange :通过添加属性key-value匹配
        DirectExchange:按照routingkey分发到指定队列
        TopicExchange:多关键字匹配
     */
    @Bean
    public DirectExchange defaultExchange() {
     return new DirectExchange(EXCHANGE, true, false);
    }

    /**
     * 配置消息队列2
     * 针对消费者配置
     * @return
     */
    @Bean
    public Queue queue() {
       return new Queue("delay_queue2", true); //队列持久

    }
    /**
     * 将消息队列2与交换机绑定
     * 针对消费者配置
     * @return
     */
    @Bean
    @Autowired
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(defaultExchange()).with(DelayQueue.ROUTINGKEY2);
    }

    /**
     * 接受消息的监听,这个监听会接受消息队列1的消息
     * 针对消费者配置
     * @return
     */
    @Bean
    @Autowired
    public SimpleMessageListenerContainer messageContainer2(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
        container.setQueues(queue());
        container.setExposeListenerChannel(true);
        container.setMaxConcurrentConsumers(1);
        container.setConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置确认模式手工确认
        container.setMessageListener(new ChannelAwareMessageListener() {

   public void onMessage(Message message, com.rabbitmq.client.Channel channel) throws Exception {
    byte[] body = message.getBody();
                System.out.println("delay_queue2 收到消息 : " + new String(body));
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); //确认消息成功消费

   }

        });
        return container;
    }


}

在消息监听中处理需要定时处理的任务就好了,因为Rabbitmq能发送消息,所以可以把任务特征码发过来,比如关闭订单就把订单id发过来,这样就避免了需要查询一下那些订单需要关闭而加重MySQL负担了,毕竟一旦订单量大的话,查询本身也是一件很费IO的事情

总结

基于Rabbitmq实现定时任务,就是将消息设置一个过期时间,放入一个没有读取的队列中,让消息过期后自动转入另外一个队列中,监控这个队列消息的监听处来处理定时任务具体的操作



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

兄弟,艿一口,点个赞!????

一文带你理解 RocketMQ 广播模式实现机制 本文主要讲解了RocketMQ广播消息的实现机制,理解广播消息,要把握下面几点1.偏移量保存在消费者本地内存和文件中;2.广播消息不支持重试;3.从源码上看,广播模式并不能支持顺序消息;4.广播模式消费者订阅了Topic下的所有MessageQueue,不会重平衡。httpshttpshttpshttps。... 阅读详情

相关推荐

Android逆向实战:通过smali/baksmali动态注入DEX逻辑

本文详细介绍了在无源码条件下对Android应用进行动态逻辑注入的实战方法。通过使用smali和baksmali工具对DEX文件进行反编译、修改与重打包,实现在目标方法中插入自定义代码(如日志打印),并分享了定位目标、处理寄存器、避免常见错误的实用技巧与避坑指南。

weixin_29008691的博客 366

芋道框架 定时任务Job 的使用

芋道框架定时任务禁用、开启, 定时任务实例demo

weixin_43652507的博客 3655

RK3568笔记六十一:MIPI摄像头应用程序编写

V4L2设备驱动框架向应用层提供了一套统一、标准的接口规范,应用程序按照该接口规范来进行应用编程,从而使用摄像头。对于摄像头设备来说,其编程模式如下所示:1.首先是打开摄像头设备;2.查询设备的属性或功能;3.设置设备的参数,譬如像素格式、帧大小、帧率;4.申请帧缓冲、内存映射;5.帧缓冲入队;6.开启视频采集;7.帧缓冲出队、对采集的数据进行处理;8.处理完后,再次将帧缓冲入队,往复;9.结束采集。

1004

rabbit mq消费者怎么限制 速度

限制消费者每秒从队列拉取的消息的数量 如果并发数量很高,那么这个时候队列中就会有很多消息等待处理,如果不限制消费者的拉取数量,消费者就会每秒拉取很多的消息,最后还是会达到一个很高的并发数,消费者服务器照样存在崩溃的可能性。 使用前提:消费者采用的是手动确认模式 修改配置文件,这里使用的是yml格式 spring: rabbitmq: listener: simple: acknowledge-mode: manual prefet...

qq690452074的专栏 2316

RT-DETR的AIF模块+YOLOv8:融合注意力与特征交互,打造实时检测的科研级进阶方案

从理解AIF模块“细粒度注意力+跨尺度交互”的核心设计,到亲手将其嵌入YOLOv8并验证在复杂场景的表现,再到基于此探索属于自己的科研与工程方向——这个过程能让你深刻理解“注意力机制与CNN融合”在实时检测中的核心价值。现在就开始行动,相信你会在实时目标检测的“智能进化”之路上,走出一条属于自己的技术路径。从实验对比图可见,集成AIF模块的YOLOv8,其mAP(平均精度)在多个阈值下均有提升,尤其是mAP50-95(高IoU阈值下的精度)提升更为显著,充分证明了AIF对细粒度特征的捕捉能力。

m0_64661770的博客 190

QML 果冻抖动:用 Scale 动画模拟 Q 弹反馈

介绍果冻抖动效果:点击后卡片通过多段 ParallelAnimation 实现 Scale X/Y 反向缩放和逐步衰减,模拟果冻弹性形变;进阶版本加入小幅 Rotation 增加活泼感,适合游戏、儿童应用等趣味反馈场景。

u011186532的专栏 426

5.rabbitMQ的高级特性:限制消费者拉取个数,设置消息过期时间,死信交换机,延迟队列

rabbitMQ的高级特性 目录rabbitMQ的高级特性1. 限制消费者每秒从队列拉取的消息的数量2. 设置队列/消息的过期时间3. 死信交换机 使用这些特性解决一些特定的问题 1. 限制消费者每秒从队列拉取的消息的数量 如果并发数量很高,那么这个时候队列中就会有很多消息等待处理,如果不限制消费者的拉取数量,消费者就会每秒拉取很多的消息,最后还是会达到一个很高的并发数,消费者服务器照样存在崩溃的可能性。 使用前提:消费者采用的是手动确认模式 修改配置文件,这里使用的是yml格式 spring: rab

cxrlover的博客 2361

【华为OD机试真题 2025B卷】662、静态扫描 | 机试真题+思路参考+代码解析(C++、Java、Py)(B卷复用)

【华为OD机试真题 2025B卷】662、静态扫描 | 机试真题+思路参考+代码解析(C++、Java、Py)(B卷复用) 华为od机试,华为od机试真题,华为od机试E卷,华为odE卷,华为od机试真题E卷,华为OD,华为OD机试,华为OD机试真题,华为od机考,华为od机试D卷,华为od机试真题D卷,华为od手撕代码,华为od,华为od机试C卷,华为od e卷,华为2024e卷,od机试,2024E卷,华为OD机试E卷,什么是华为od,华为od介绍,全面了解华为od,2024华为od,如何备战华为od

KJ.JK 5398

boot定时任务开启和关闭 spring_延迟队列实现,定时任务,关闭订单

场景开发中经常需要用到定时任务,对于商城来说,定时任务尤其多,比如优惠券定时过期、订单定时关闭、微信支付2小时未支付关闭订单等等,都需要用到定时任务,但是定时任务本身有一个问题,一般来说我们都是通过定时轮询查询数据库来判断是否有任务需要执行,也就是说不管怎么样,我们需要先查询数据库,而且有些任务对时间准确要求比较高的,需要每秒查询一次,对于系统小倒是无所谓,如果系统本身就大而且数据也多的情况下,这...

weixin_42353187的博客 379

摩尔线程嵌入式面试题及参考答案(2万字长文)

说一下你对 drm 框架的理解。 DRM(Direct Rendering Manager)是 Linux 系统中用于管理图形显示设备的一个重要框架。 从架构层面来讲,它处于内核空间,主要目的是为用户空间的图形应用程序提供一个统一的接口来访问图形硬件。DRM 包括内核态的驱动模块和用户态的库。内核态的驱动负责和硬件进行交互,比如对显存的管理、硬件寄存器的配置等。用户态的库则方便应用程序利用内核态提供的功能进行图形渲染等操作。 在图形显示的具体流程中,DRM 可以控制显示设备的分辨率、刷

大模型大数据攻城狮的专栏 1229

【DEIM创新改进】全网独家创新、注意力改进篇 | AAAI 2026 | DEIM 引入轻量级群体注意力(LWGA)模块,可以显著提高其多尺度物体检测能力,特别是在小物体和复杂背景下的表现

本文提出了一种改进的DEIM网络模型,通过引入轻量级LWGA模块显著提升了多尺度物体检测性能。LWGA模块采用局部和全局注意力机制,有效提升了小物体和复杂背景下的识别能力,同时保持低计算成本。该模块通过分割输入特征图并应用多种注意力机制(GPA、RLA、SMA、SGA)进行特征提取和融合,优化了多尺度物体的特征表示。实验表明,改进后的DEIM模型在遥感图像处理等任务中表现出色,在12个数据集上实现了性能与计算效率的最佳平衡,为资源受限设备提供了高效的解决方案。

Ai缝合怪、助力小伙伴高效发论文 274

rabbitmq 延迟队列_延迟队列实现,定时任务,关闭订单

场景开发中经常需要用到定时任务,对于商城来说,定时任务尤其多,比如优惠券定时过期、订单定时关闭、微信支付2小时未支付关闭订单等等,都需要用到定时任务,但是定时任务本身有一个问题,一般来说我们都是通过定时轮询查询数据库来判断是否有任务需要执行,也就是说不管怎么样,我们需要先查询数据库,而且有些任务对时间准确要求比较高的,需要每秒查询一次,对于系统小倒是无所谓,如果系统本身就大而且数据也多的...

weixin_39581845的博客 108

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

订单到期关闭如何实现

【代码】订单到期关闭如何实现。

szuaudi的博客 762

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

如何优雅的通过Spring Boot+Redission对订单实现定时关闭

在电子商务及支付相关平台中,常规流程是首先生成订单或支付请求,用户随后会在规定时间内完成支付。如果用户未能在预设时限内完成支付动作,系统通常会执行相应的过期处理机制,即自动取消未支付的订单。到期自动确认收货:当商品配送预计到达时间过后,若用户未主动确认收货,系统将自动视为用户已收货并触发后续评价或售后环节。超时自动退款:对于某些未及时处理的退款申请,或者因物流异常等原因导致的退款,系统在达到预设等待期限后会自动退还消费者款项。

yiridancan的博客 2306

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

别再用定时任务来关闭超时订单了

前言 在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作,细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在1s内;那他们是怎么实现的呢? 业务场景 我们以订单功能为例说明下:生成订单后一段时间不支付订单会自动关闭。最简单的想法是设置定时任务轮询,但是每个订单的创建时间不一样,定时任务的规则无法设定,如果将定时任务执行的间隔设置的过短,太影响效率。还有一种想法,在用户进入订单界面的时候,判断时间执行相关操作。 一、定时任务 一般情况下,最

Monsterof的博客 4471

领导:谁再用 Redis 实现过期订单关闭,立马滚蛋!

点击上方“芋道源码”,选择“设为星标”管她前浪,还是后浪?能浪的浪,才是好浪!每天 10:33更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT 源码解析作业调度中间件 E...

芋艿V 440

定时任务方案的正确选取

在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作,细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在1s内;那他们是怎么实现的呢?定时任务关闭订单rocketmq延迟队列rabbitmq死信队列时间轮算法redis过期监听。

justlpf的专栏 461

电商平台中订单未支付过期如何实现自动关闭?

在日常开发中,我们经常会遇到这种业务场景,如:外卖订单超过30分钟未支付,则自动取消订单;用户注册成功15分钟后,发送信息通知用户等等,这就是延时任务的处理场景。在电商、支付等系统中,一般都是先创建订单,再给用户一定时间进行支付,如果用户没有按时支付的话,就需要把之前的订单关闭。这种场景还有很多,比如到期自动收货,超时自动退款,下单后自动发送短信等类似的任务问题。

qq_57395198的博客 1490
上一篇: 求求你不要在用 !=null 判空了
下一篇: 2020 年 10 月程序员工资统计,终于涨了!
公众号-芋道源码
博客等级 码龄10年 1634粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值