Redis实现延迟队列

目录

一、什么是延时队列

二、延时队列的应用

三、举例说明

我的设计思想:


一、什么是延时队列


   延时队列相比于普通队列最大的区别就体现在其延时的属性上,普通队列的元素是先进先出,按入队顺序进行处理,而延时队列中的元素在入队时会指定一个延迟时间,表示其希望能够在经过该指定时间后处理或者是在某个时间进行处理。

二、延时队列的应用

  1. 12306 下单成功后,在半个小时内没有支付,自动取消订单。
  2. 如果订单一直处于某一个未完结状态时,及时处理关单,并退还库存。
  3. 用户下单外卖以后,距离超时时间还有 10 分钟时提醒外卖小哥即将超时。
  4. 外卖平台发送订餐通知,下单成功后 60s 给用户推送短信。
  5. 规定某个时间执行某个任务

三、举例说明

以下是我做项目时遇到的例子:  需求就是用户设定时间,到时间之后,系统自动执行某个任务

我的设计思想:

采用轮询的策略监听redis的key的值,将用户输入的时间在后端转换为一个时间戳,利用redis Zset的数据结构来存储,主要用来判断的就是时间戳,Zset是一个有序的集合,所有时间戳在前面的就是先要执行的事件,当然用时间戳来比较的话,就是从0到现在时间的时间戳来比较大小,如何redis存入的时间戳大于0且 小于当前时间戳就代表执行任务,否则就代表待执行


首先,封装了一个实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DelayMessageVo implements Serializable {

    /**
     * 切记实例化
     */
    private static final long serialVersionUID = -7671756385477179547L;

    /**
     * 消息 id
     */
    private Integer id;

    /**
     * 消息内容
     */
    private String content;

    /**
     * 消息到期时间
     */
    private long expireTime;

}

 对Redis进行操作

@Component
public class DelayQueueService {

    /**
     * key后面拼接当前机器的内网ip : 用于集群区分,解决集群出现的并发问题
     */
    private static final String KEY = "delay_queue:" + getHostAddress();

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 添加消息到延时队列中
     */
    public void put(DelayMessageVo message ) {
        redisTemplate.opsForZSet().add(KEY, message, message.getExpireTime());
    }

    /**
     * 从延时队列中删除消息
     */
    public Long remove(DelayMessageVo message) {
        Long remove = redisTemplate.opsForZSet().remove(KEY, message);
        return remove;
    }

    /**
     * 获取延时队列中已到期的消息
     */
    public List<DelayMessageVo> getExpiredMessages() {
//        1 : 获取到开始时间
        long minScore = 0;
//        2 : 获取当前时间
        long maxScore = System.currentTimeMillis();
//        3 : 获取到指定范围区间的数据列表
        Set<Object> messages = redisTemplate.opsForZSet().rangeByScore(KEY, minScore, maxScore);
        if (messages == null || messages.isEmpty()) {
            return Collections.emptyList();
        }
//        4 : 把对象进行封装,返回
        List<DelayMessageVo> result = new ArrayList<>();
        for (Object message : messages) {
            // 将 DelayMessageVo 对象转换为 JSON 字符串
            String jsonMessage = JSON.toJSONString(message);
            DelayMessageVo delayMessage = JSONObject.parseObject(jsonMessage, DelayMessageVo.class);
            result.add(delayMessage);
        }
        return result;
    }

    /**
     * 获取地址(服务器的内网地址)(内网ip)
     *
     * @return
     */
    public static String getHostAddress() {
        InetAddress localHost = null;
        try {
            localHost = InetAddress.getLocalHost();
        } catch (
                 UnknownHostException e) {
            e.printStackTrace();
        }
        return localHost.getHostAddress();
    }
}

 轮询策略

    @Component
    public class DelayMessageHandler {

        public static SimpleDateFormat dateTimeFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        @Autowired
        private DelayQueueService delayQueue;

        @Autowired
        private ExamineMapper examineMapper;

    /**
     * 处理已到期的消息(轮询)
     */
    @Scheduled(fixedDelay = 60000)
    public void handleExpiredMessages() {
        String currentTime = getCurrentTime();
//      1 : 扫描任务,并将需要执行的任务加入到任务队列中
        List<DelayMessageVo> messages = delayQueue.getExpiredMessages();
        System.out.println(currentTime + " 待处理消息数量:" + messages.size());
//      2 : 开始处理消息
        if (!messages.isEmpty()) {
            for (DelayMessageVo message : messages) {
//                2.1 : 处理消息:先删除消息,获取当前消息是否已经被其他人消费
                Long remove = delayQueue.remove(message);
                if (remove > 0) {
//                2.2 : 开启线程异步处理消息:不让处理消息的时间阻塞当前线程
                    new Thread(() -> {
                        System.out.println(currentTime + " :" + message.getId() + " --> 消息开始处理");
                        Integer id = message.getId();
                        String content = message.getContent();
                        if (content.equals("任务开始时间")){
                            examineMapper.updateBeginExamineStatus(id);
                        }else if (content.equals("任务结束时间")){
                            examineMapper.updateFinishExamineStatus(id);
                        }else if (content.equals("公告结束时间")){
                            examineMapper.updatePublicityExamineStatus(id);
                        }
                        try {
//                      2.1.1 : 模拟睡眠3秒,任务的处理时间(实际可能会更长)
                            Thread.sleep(3000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        System.out.println(currentTime + " :" + message.getId() + " --> 消息处理结束");
                    }).start();
                }
            }
        }
    }
        /**
         * 获取到的当前时分秒
         *
         * @return
         */
        public static String getCurrentTime() {
            String format = dateTimeFormater.format(new Date());
            return format;
        }
    }

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

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

相关文章

魔众文库系统v5.7.0版本文件顺序选择,短信注册支持设置密码,前端界面升级

文件顺序选择&#xff0c;短信注册支持设置密码&#xff0c;前端界面升级 [新功能] 富文本支持文档一键导入&#xff0c;支持Word文档&#xff08;docx&#xff09;、Markdown文档&#xff08;md&#xff09; [新功能] 财务中心→全部订单新增"业务订单ID"筛选条件…

10天玩转Python第7天:python 面向对象 全面详解与代码示例

今日内容 封装(定义类的过程) 案例(存放家具) 继承 多态 封装的补充 私有和公有权限属性的分类(实例属性, 类属性)方法的分类(实例方法, 类方法, 静态方法) 封装案例 # 定义家具类 class HouseItem: """家具类""" def __init__(self, name, a…

linux应用层编程问题--沙雕问题

1.调用沁恒 USB读取接口 读不到数据 static bool CH37XASyncReadData(int iIndex, uint32_t epindex, void *oBuffer, uint32_t *ioLength) {struct _bulkUp {uint32_t len;uint8_t epindex;uint8_t data[0];} __attribute__((packed));struct _bulkUp *bulkUp;int retval;bul…

zookeeper基础内容

文章目录 Zookeeper基础概述数据结构Zookeeper节点操作zookeeper节点操作命令数据模型 znode 结构 zookeeper java客户端ZooKeeper原生APICuratorzkClient对比总结 Zookeeper基础 概述 zookeeper&#xff08;分布式协调服务&#xff09; 本质&#xff1a;小型的文件存储系统监…

Android13适配所有文件管理权限

Android13适配所有文件管理权限 前言&#xff1a; 很早之前在Android11上面就适配过所有文件管理权限&#xff0c;这次是海外版升级到Android13&#xff0c;由于选择相册用的是第三方库&#xff0c;组内的同事没有上架Google的经验直接就提交代码&#xff0c;虽然功能没有问题…

3.qml 3D-Node类学习

Node类是在View3D 中的对象基础组件&#xff0c;用于表示3D空间中的对象&#xff0c;类似于Qt Quick 2D场景中的Item&#xff0c;介绍如下所示&#xff1a; 如上图可以看到&#xff0c;Node类的子类非常多&#xff0c;比如Model类(显示3D模型)、ParticleSystem3D粒子系统类、Li…

Guardrails for Amazon Bedrock 基于具体使用案例与负责任 AI 政策实现定制式安全保障(预览版)

作为负责任的人工智能&#xff08;AI&#xff09;战略的一部分&#xff0c;您现在可以使用 Guardrails for Amazon Bedrock&#xff08;预览版&#xff09;&#xff0c;实施专为您的用例和负责任的人工智能政策而定制的保障措施&#xff0c;以此促进用户与生成式人工智能应用程…

C#爬虫1688以图搜图API接口功能的实现

背景 在1688有个功能&#xff0c;就是上传图片&#xff0c;就可以找到类似的商品。如下 网址 &#xff1a;https://www.1688.com/ 这时候&#xff0c;我们可以使用程序来代替&#xff0c;大批量的完成图片上传功能。 实现思路 1、找到图片上传接口 post请求&#xff0c;for…

R、python读取空间转录组的8种方式

“ 空间转录组测序主要包括5个步骤&#xff0c;我们着重下游分析部分&#xff1a;空转数据分析和可视化。本篇主分享如何使用python和R读取空转数据&#xff0c;主要使用scanpy stlearn seurat包” 引言 在正式开始之前&#xff0c;我们先看看cellranger流程跑完之后&#xff0…

杰卡德的故事

三个男人分别是杰卡德距离 杰卡德相似系数和杰卡德系数 杰卡德相似系数和杰卡德距离是互为相反数的。 杰卡德系数和杰卡德距离是不是一回事 感觉是一回事

【论文阅读】Uncertainty-aware Self-training for Text Classification with Few Label

论文下载 GitHub bib: INPROCEEDINGS{mukherjee-awadallah-2020-ust,title "Uncertainty-aware Self-training for Few-shot Text Classification",author "Subhabrata Mukherjee and Ahmed Hassan Awadallah",booktitle "NeurIPS",yea…

mybatis高级扩展-插件和分页插件PageHelper

1、建库建表 create database mybatis-example; use mybatis-example; create table emp (empNo varchar(40),empName varchar(100),sal int,deptno varchar(10) ); insert into emp values(e001,张三,8000,d001); insert into emp values(e002,李四,9000,d001); insert into…

OpenHarmony应用开发——创建第一个OpenHarmonry工程

一、前言 本文主要介绍DevEco Studio的相关配置&#xff0c;以及创建第一个OpenHarmony应用程序。 二、详细步骤 打开DevEco Studio. 进入Settings. 随后SDK选择OpenHarmony&#xff0c;并完成下述API的选择与下载. 等待下载完成后&#xff0c;创建第一个Project. 此处选择Emp…

在React中实现好看的动画Framer Motion(案例:跨DOM元素平滑过渡)

前言 介绍 Framer Motion 是一个适用于 React 网页开发的动画库&#xff0c;它可以让开发者轻松地在他们的项目中添加复杂和高性能的动画效果。该库提供了一整套针对 React 组件的动画、过渡和手势处理功能&#xff0c;使得通过声明式的 API 来创建动画变得简单直观。 接下来…

ChatGPT4 Excel 高级组合函数用法index+match完成实际需求

在Excel 函数用法中有一对组合函数使用是非常多的,那就是Index+match组合函数。 接下来我们用一个实际的需求让ChatGPT来帮我们实现一下。 我们给ChatGPT4发送一个prompt:有一个表格A2至A14为业务员B列至H列为1月至7月的销售额,请根据J2单元格的业务员与K2单元格的月份查找出…

DevOps搭建(二)-阿里云镜像仓库的使用详解

博主介绍&#xff1a;Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 &#x1f345;文末获取源码下载地址&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb;…

使用令牌桶和漏桶实现请求限流逻辑

实现请求限流 令牌桶算法原理实现案例案例目的:实例demo运行结果: 漏桶算法原理:实现案例:案例目的:案例代码运行结果: 令牌桶算法和漏桶算法是两种常用的限流算法&#xff0c;用于控制系统对请求或数据的访问速率。下面分别详细解释这两种算法的原理. 令牌桶算法 原理 令牌桶…

前端传递参数,后端如何接收

目录 简单参数 传递方式 获取方式一 获取方式二 相关注解 实体参数 数组集合参数 传递方式 相关注解 获取方式一 获取方式二 日期参数 传递方式 相关注解 获取方式 json参数 传递方式 相关注解 获取方式 路径参数 传递方式 相关注解 获取方式 传递多个…

DHCP最全讲解!(原理+配置)

一、概述 随着网络规模的不断扩大&#xff0c;网络复杂度不断提升&#xff0c;网络中的终端设备例如主机、手机、平板等&#xff0c;位置经常变化。终端设备访问网络时需要配置IP地址、网关地址、DNS服务器地址等。采用手工方式为终端配置这些参数非常低效且不够灵活。IETF于19…

day04-报表技术PDF

1 EasyPOI导出word 需求&#xff1a;使用easyPOI方式导出合同word文档 Word模板和Excel模板用法基本一致&#xff0c;支持的标签也是一致的&#xff0c;仅仅支持07版本的word也是只能生成后缀是docx的文档&#xff0c;poi对doc支持不好所以easyPOI中就没有支持doc&#xff0c…