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

基于redis实现延迟队列

Redis实现延时队列

延时队列里装的主要是延时任务,用延时队列来维护延时任务的执行时间。

1、延时队列有哪些使用情景?

1、如果请求加锁没加成功

可以将这个请求扔到延时队列里,延后处理。

2、业务中有延时任务的需要

比如说,文章定时发布。

2、基于redis实现延时队列

2.1.使用zset实现

  • 使用redis的zset来实现延时队列

  • 使用zset的添加、查询、删除命令

    • ZADD命令:

    ZADD key score member [score member...]
    • ZRANGEBYSCORE命令:

    ZRANGEBYSCORE key min max
    • ZREM命令:

    ZREM key member [member ...]
  • 1)将延时任务的【到期执行时间作为zset的score】、【延时任务序列化为一个字符串作为zset的member】使用zadd命令装进zset中。

  • 2)zset会为这些延时任务按照到期执行时间排序。

  • 3)设置多线程轮询zset获取到期的任务 | 用@Schedule注解标注定时轮询

  • 4)在zset中删除这些到期任务

  • 5)将这些到期任务放进list中,在list中使用【blpop/brpop】消费任务。

优化1:

对于第3步来说,由于是多线程,所以同一个到期任务可能会被多个进程获取到,然后再使用 zrem 进行争抢。最终一定只有一个进程zrem成功。因此对于那些zrem没成功的进程相当于白取任务。所以我们可以进一步优化:【使用 lua scripting 让 zrangebyscore 和 zrem 一同挪到服务器端进行原子化操作】。

优化2:

使用redis管道操作:通过管道方式将获取到的到期任务push到list中。

优化:redis管道
1)Redis的消息交互

客户端将请求传送给服务器,服务器处理完毕后,再将响应回复给客户端。这要花费一个网络数据包来回的时间。

详细过程是这样的:

1、客户端进程调用 write 将消息写到操作系统内核为套接字分配的发送缓冲 send buffer。

2、客户端操作系统内核将发送缓冲的内容发送到网卡,网卡硬件将数据通过「网际路由」送到服务器的网卡。

3、服务器操作系统内核将网卡的数据放到内核为套接字分配的接收缓冲 recv buffer。

4、服务器进程调用 read 从接收缓冲中取出消息进行处理。

5、服务器进程调用 write 将响应消息写到内核为套接字分配的发送缓冲 send buffer。

6、服务器操作系统内核将发送缓冲的内容发送到网卡,网卡硬件将数据通过「网际路由」送到客户端的网卡。

7、客户端操作系统内核将网卡的数据放到内核为套接字分配的接收缓冲 recv buffer。

8、客户端进程调用 read 从接收缓冲中取出消息返回给上层业务逻辑进行处理。

9、结束。

由上面的详细过程,可以知道:

【write操作】只负责将数据写到发送缓冲。但是如果发送缓冲满了,那么就需要等待缓冲空出空闲空间。这个就是写操作 IO 操作的真正耗时。

【read操作】只负责将数据从接收缓冲中取出来就完事了。如果缓冲是空的,那么就需要等待数据到来,这个就是读操作 IO 操作的真正耗时。

因此,一条消息的消息交互所花费的时间主要在于:

write 操作几乎没有耗时,直接写到发送缓冲就返回,而 read 就会比较耗时了,因为它要等待消息经过网络路由到目标机器处理后的响应消息,再回送到当前的内核读缓冲才可以返回。这才是一个网络来回的真正开销。

2)连续多条消息指令的消息交互

(1)普通方式:

如果要执行多条消息指令,则需要花费多个网络来回的时间。

(2)使用redis管道:

管道方式是客户端通过将多条命令white到发送缓存中,然后再一次性发送到服务端的缓存中,服务端处理完这些命令后将响应结果写到发送缓存中,最后再发送给接收缓存,客户端第一次read的时候,需要等待一个网络来回,而后续的read操作直接从接收缓存中取就行了,因此总的花费时间只有一个网络来回的时间。

以上两种方式的示例代码:

public class RedisPipelineTestDemo {public static void main(String[] args) {//连接redisJedis jedis = new Jedis("10.101.17.180", 6379);//jedis逐一给每个set新增一个valueString zSetKey = "Pipeline-test-set";int size = 100000;//普通方式long begin = System.currentTimeMillis();for (int i = 0; i < size; i++) {jedis.sadd(zSetKey + i, "aaa");}log.info("Jedis逐一给每个set新增一个value耗时:{}ms", (System.currentTimeMillis() - begin));//管道方式      Pipeline Pipeline = jedis.Pipelined();begin = System.currentTimeMillis();for (int i = 0; i < size; i++) {             Pipeline.sadd(zSetKey + i, "bbb");}         Pipeline.sync();log.info("Jedis Pipeline模式耗时:{}ms", (System.currentTimeMillis() - begin));}
}
​
普通方式:162655ms
管道方式:504ms

2.2.使用redis键空间通知

redis的PubSub,发布者订阅者模型。

摘自javaguide:

在 pub/sub 模式下,生产者需要指定消息发送到哪个 channel 中,而消费者则订阅对应的 channel 以获取消息。

Redis 中有很多默认的 channel,这些 channel 是由 Redis 本身向它们发送消息的,而不是我们自己编写的代码。其中,__keyevent@0__:expired 就是一个默认的 channel,负责监听 key 的过期事件。也就是说,当一个 key 过期之后,Redis 会发布一个 key 过期的事件到__keyevent@<db>__:expired这个 channel 中。

我们只需要监听这个 channel,就可以拿到过期的 key 的消息,进而实现了延时任务功能。

这个功能Redis官方称为keyspace notifications,字面意思就是键空间通知。

代码实现:

Spring已经实现了监听__keyevent@*__:expired这个channel这个功能,__keyevent@*__:expired中的*代表通配符的意思,监听所有的数据库。

在配置类中:

@Configuration
public class RedisConfiguration {
​@Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();redisMessageListenerContainer.setConnectionFactory(connectionFactory);return redisMessageListenerContainer;}
​@Beanpublic KeyExpirationEventMessageListener redisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) {return new KeyExpirationEventMessageListener(redisMessageListenerContainer);}/**KeyExpirationEventMessageListener实现了对__keyevent@*__:expiredchannel的监听当KeyExpirationEventMessageListener收到Redis发布的过期Key的消息的时候,会发布RedisKeyExpiredEvent事件**/
​
}

所以我们只需要监听RedisKeyExpiredEvent事件就可以拿到过期消息的Key,也就是延迟消息。对RedisKeyExpiredEvent事件的监听实现MyRedisKeyExpiredEventListener。

@Component
public class MyRedisKeyExpiredEventListener implements ApplicationListener<RedisKeyExpiredEvent> {
​@Overridepublic void onApplicationEvent(RedisKeyExpiredEvent event) {byte[] body = event.getSource();System.out.println("获取到延迟消息:" + new String(body));}
​
}

缺点:

1、能否及时的监听到过期键过期,取决于【redis的过期键删除策略】。由于可能出现redis过期键已经过期,但是redis还未将其删除从而导致无法监听到过期键过期。使得最后消息延迟。

补充:redis过期键删除策略
  • 定时删除

  • 惰性删除

1)定时删除

Redis 默认会每秒进行十次过期扫描,过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。

1、从过期字典中随机 20 个 key;

2、删除这 20 个 key 中已经过期的 key;

3、如果过期的 key 比率超过 1/4,那就重复步骤 1;

同时,为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过【 25ms】。

大量key同时过期存在的问题:

但是如果一个Redis 实例中所有的 key 在同一时间过期,即使有25ms的扫描时间上限,如果此时有101 个客户端同时将请求发过来,25ms后才能处理一个客户端的请求,然后25ms后再处理一个客户端的请求,那么第101个客户端需要等待2500ms后才能被处理请求。

大量key同时过期解决方法:

给过期时间设置一个随机范围,而不能全部在同一时间过期。

2)惰性删除

在客户端访问这个 key 的时候,redis 对 key 的过期时间进行检查,如果过期了就立即删除。

参考:

《Redis深度历险》

Redis常见面试题总结(上) | JavaGuide

相关文章:

基于redis实现延迟队列

Redis实现延时队列 延时队列里装的主要是延时任务&#xff0c;用延时队列来维护延时任务的执行时间。 1、延时队列有哪些使用情景&#xff1f; 1、如果请求加锁没加成功 可以将这个请求扔到延时队列里&#xff0c;延后处理。 2、业务中有延时任务的需要 比如说&#xff0…...

PHP微信小程序共享充电桩系统设计与实现计算机毕业设计源代码作品和开题报告

博主介绍&#xff1a;黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者&#xff0c;CSDN博客专家&#xff0c;在线教育专家&#xff0c;CSDN钻石讲师&#xff1b;专注大学生毕业设计教育、辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…...

【网络面试篇】TCP与UDP类

目录 一、综述 1. TCP与UDP的概念 2. 特点 3. 区别 4. 对应的使用场景 二、补充 1. 基础概念 &#xff08;1&#xff09;面向连接 &#xff08;2&#xff09;可靠的 &#xff08;3&#xff09;字节流 2. 相关问题 &#xff08;1&#xff09;TCP 和 UDP 可以同时绑定…...

Windows转Mac过渡指南

最近由于工作原因开始使用mac电脑&#xff0c;说实话刚拿到手的时候&#xff0c;window党表示真的用不惯。坚持用一下午之后&#xff0c;发现真的yyds&#xff0c;这篇文章说说mac电脑的基本入门指南。 1. 不会使用mac的触摸板&#xff0c;接上鼠标发现滚轮和windows是反的。 …...

LeetCode100之盛最多水的容器(11)--Java

1.问题描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量 注意 你不能倾斜容器 示例1 输入&…...

【VMware】使用笔记

一、安装 win11支持16.2以上版本&#xff0c;其他版本不兼容 安装参考&#xff1a; 二、设置 1、蓝屏设置 参考&#xff1a;win11打开VMware虚拟机蓝屏解决_win11vmware蓝屏-CSDN博客 2、VMwareTool配置 第一步&#xff1a;移除“open-vm-tools” sudo apt-get autoremo…...

<项目代码>YOLOv8 猫狗识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…...

存储数据库的传输效率提升-ETLCloud结合HBASE

一、大数据存储数据库–HBASE HBase&#xff0c;作为一个开源的分布式列存储数据库&#xff0c;基于Google的Bigtable设计而成&#xff0c;专为处理大规模结构化数据而优化。使用HBase打造大数据解决方案的好处主要包括&#xff1a;高可扩展性&#xff0c;能够处理PB级的数据&…...

HO-XGBoost河马算法优化极限梯度提升树多变量回归预测(Matlab)

HO-XGBoost河马算法优化极限梯度提升树多变量回归预测&#xff08;Matlab&#xff09; 目录 HO-XGBoost河马算法优化极限梯度提升树多变量回归预测&#xff08;Matlab&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现HO-XGBoost多变量回归预测&…...

【Hive sql面试题】找出连续活跃3天及以上的用户

表数据如下&#xff1a; 要求&#xff1a;求出连续活跃三天及以上的用户 建表语句和插入数据如下&#xff1a; create table t_useractive(uid string,dt string );insert into t_useractive values(A,2023-10-01 10:10:20),(A,2023-10-02 10:10:20),(A,2023-10-03 10:16…...

Linux curl命令下载显示时间/速度/大小

命令&#xff1a; curl -# -O --compressed -w "大小: %{size_download} bytes\n时间: %{time_total} seconds\n速度: %{speed_download} B/s\n" 下载URL链接。 例子&#xff1a; curl -# -O --compressed -w "大小: %{size_download} bytes\n时间: %{time_to…...

sklearn|机器学习:决策树(一)

文章目录 sklearn&#xff5c;机器学习&#xff1a;决策树&#xff08;一&#xff09;&#xff08;一&#xff09;概述&#xff08;二&#xff09;实战1. 环境配置2. sklearn 中的决策树&#xff08;1&#xff09;模块 sklearn.tree&#xff08;2&#xff09;sklearn 基本建模流…...

Rust中三种方式使用环境变量

环境变量是存储在操作系统中的一组键值对。它们用于存储系统和其他应用程序所需的配置信息。本文我们将探索如何在Rust中使用标准库以及dotenv crate来处理环境变量。 环境变量 环境变量提供了一种灵活的方式来配置应用程序&#xff0c;而无需直接在源代码中硬编码配置值。这…...

搭建支持国密GmSSL的Nginx环境

准备 1、服务器准备&#xff1a;本文搭建使用的服务器是CentOS 7.6 2、安装包准备&#xff1a;需要GmSSL、国密Nginx&#xff0c;可通过互联网下载或者从 https://download.csdn.net/download/m0_46665077/89936158 下载国密GmSSL安装包和国密Nginx安装包。 服务器安装依赖包…...

Docker部署Portainer CE结合内网穿透实现容器的可视化管理与远程访问

文章目录 前言1. 本地安装Docker2. 本地部署Portainer CE3. 公网远程访问本地Portainer-CE3.1 内网穿透工具安装3.2 创建远程连接公网地址4. 固定Portainer CE公网地址前言 本篇文章介绍如何在Ubuntu中使用docker本地部署Portainer CE可视化管理工具,并结合cpolar实现公网远程…...

不适合的学习方法

文章目录 不适合的学习方法1. 纯粹死记硬背2. 过度依赖单一资料3. 线性学习4. 被动学习5. 一次性学习6. 忽视实践7. 缺乏目标导向8. 过度依赖技术9. 忽视个人学习风格10. 过于频繁的切换 结论 以下是关于不适合的学习方法的更详细描述&#xff0c;包括额外的内容和相关公式&…...

在子类中调用父类的构造函数

在Java中调用父类构造函数 使用super()关键字&#xff1a;在子类的构造函数中&#xff0c;可以使用super()来调用父类的构造函数。如果父类有默认构造函数&#xff08;即没有参数的构造函数&#xff09;&#xff0c;并且子类的构造函数没有显式调用super()&#xff0c;Java编译…...

【K8S系列】Kubernetes 中 Service 的流量不均匀问题【已解决】

在 Kubernetes 中&#xff0c;Service 是一种抽象&#xff0c;用于定义一组 Pod 的访问策略。当某些 Pod 接收的流量过多&#xff0c;而其他 Pod 的流量较少时&#xff0c;可能会导致负载不均衡。这种情况不仅影响性能&#xff0c;还可能导致某些 Pod 过载&#xff0c;影响应用…...

C-小H学生物

题意&#xff1a;一棵树节点编号为1具有n种不同物种的演化树上。物种i将遗传信息向下传递到物种j会产生dij的遍历。dij是一个长为l的01串。变异程度duv为u到v简单路径上的所有编译信息的异或和。基因多样性定义为 分析&#xff1a;计算Di的遗传信息&#xff0c;用dfs将遗传信息…...

什么是软件设计模式, 它们⽤于解决什么问题, 它们为什么有效

什么是设计模式 软件设计模式是指在软件设计过程中&#xff0c;经过验证的、可复⽤的、对特定 场景下常⻅问题的解决⽅案的⼀种描述或模板。这些模式并不是具体的 代码&#xff0c;⽽是⽤于指导如何组织代码、类和对象&#xff0c;以便更好地解决问题和 满⾜需求。 ⽤于解决的…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...