基于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实现延时队列 延时队列里装的主要是延时任务,用延时队列来维护延时任务的执行时间。 1、延时队列有哪些使用情景? 1、如果请求加锁没加成功 可以将这个请求扔到延时队列里,延后处理。 2、业务中有延时任务的需要 比如说࿰…...
PHP微信小程序共享充电桩系统设计与实现计算机毕业设计源代码作品和开题报告
博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育、辅导。 所有项目都配有从入门到精通的基础知识视频课程ÿ…...
【网络面试篇】TCP与UDP类
目录 一、综述 1. TCP与UDP的概念 2. 特点 3. 区别 4. 对应的使用场景 二、补充 1. 基础概念 (1)面向连接 (2)可靠的 (3)字节流 2. 相关问题 (1)TCP 和 UDP 可以同时绑定…...
Windows转Mac过渡指南
最近由于工作原因开始使用mac电脑,说实话刚拿到手的时候,window党表示真的用不惯。坚持用一下午之后,发现真的yyds,这篇文章说说mac电脑的基本入门指南。 1. 不会使用mac的触摸板,接上鼠标发现滚轮和windows是反的。 …...
LeetCode100之盛最多水的容器(11)--Java
1.问题描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量 注意 你不能倾斜容器 示例1 输入&…...
【VMware】使用笔记
一、安装 win11支持16.2以上版本,其他版本不兼容 安装参考: 二、设置 1、蓝屏设置 参考:win11打开VMware虚拟机蓝屏解决_win11vmware蓝屏-CSDN博客 2、VMwareTool配置 第一步:移除“open-vm-tools” sudo apt-get autoremo…...
<项目代码>YOLOv8 猫狗识别<目标检测>
YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…...
存储数据库的传输效率提升-ETLCloud结合HBASE
一、大数据存储数据库–HBASE HBase,作为一个开源的分布式列存储数据库,基于Google的Bigtable设计而成,专为处理大规模结构化数据而优化。使用HBase打造大数据解决方案的好处主要包括:高可扩展性,能够处理PB级的数据&…...
HO-XGBoost河马算法优化极限梯度提升树多变量回归预测(Matlab)
HO-XGBoost河马算法优化极限梯度提升树多变量回归预测(Matlab) 目录 HO-XGBoost河马算法优化极限梯度提升树多变量回归预测(Matlab)预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现HO-XGBoost多变量回归预测&…...
【Hive sql面试题】找出连续活跃3天及以上的用户
表数据如下: 要求:求出连续活跃三天及以上的用户 建表语句和插入数据如下: 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命令下载显示时间/速度/大小
命令: curl -# -O --compressed -w "大小: %{size_download} bytes\n时间: %{time_total} seconds\n速度: %{speed_download} B/s\n" 下载URL链接。 例子: curl -# -O --compressed -w "大小: %{size_download} bytes\n时间: %{time_to…...
sklearn|机器学习:决策树(一)
文章目录 sklearn|机器学习:决策树(一)(一)概述(二)实战1. 环境配置2. sklearn 中的决策树(1)模块 sklearn.tree(2)sklearn 基本建模流…...
Rust中三种方式使用环境变量
环境变量是存储在操作系统中的一组键值对。它们用于存储系统和其他应用程序所需的配置信息。本文我们将探索如何在Rust中使用标准库以及dotenv crate来处理环境变量。 环境变量 环境变量提供了一种灵活的方式来配置应用程序,而无需直接在源代码中硬编码配置值。这…...
搭建支持国密GmSSL的Nginx环境
准备 1、服务器准备:本文搭建使用的服务器是CentOS 7.6 2、安装包准备:需要GmSSL、国密Nginx,可通过互联网下载或者从 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. 过于频繁的切换 结论 以下是关于不适合的学习方法的更详细描述,包括额外的内容和相关公式&…...
在子类中调用父类的构造函数
在Java中调用父类构造函数 使用super()关键字:在子类的构造函数中,可以使用super()来调用父类的构造函数。如果父类有默认构造函数(即没有参数的构造函数),并且子类的构造函数没有显式调用super(),Java编译…...
【K8S系列】Kubernetes 中 Service 的流量不均匀问题【已解决】
在 Kubernetes 中,Service 是一种抽象,用于定义一组 Pod 的访问策略。当某些 Pod 接收的流量过多,而其他 Pod 的流量较少时,可能会导致负载不均衡。这种情况不仅影响性能,还可能导致某些 Pod 过载,影响应用…...
C-小H学生物
题意:一棵树节点编号为1具有n种不同物种的演化树上。物种i将遗传信息向下传递到物种j会产生dij的遍历。dij是一个长为l的01串。变异程度duv为u到v简单路径上的所有编译信息的异或和。基因多样性定义为 分析:计算Di的遗传信息,用dfs将遗传信息…...
什么是软件设计模式, 它们⽤于解决什么问题, 它们为什么有效
什么是设计模式 软件设计模式是指在软件设计过程中,经过验证的、可复⽤的、对特定 场景下常⻅问题的解决⽅案的⼀种描述或模板。这些模式并不是具体的 代码,⽽是⽤于指导如何组织代码、类和对象,以便更好地解决问题和 满⾜需求。 ⽤于解决的…...
网易云音乐NCM转MP3终极指南:ncmdump工具完整使用教程
网易云音乐NCM转MP3终极指南:ncmdump工具完整使用教程 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经从网易云音乐下载了心爱的歌曲,却发现只能在特定播放器上收听?NCM格式的限制让音乐…...
别再乱算相似度了!用Python实战二元变量聚类:从Jaccard系数到病人分组
医疗数据分析实战:用Python实现基于Jaccard系数的病人症状聚类在医疗数据分析领域,如何从海量病人症状数据中发现潜在规律一直是临床研究的难点。传统方法往往依赖医生经验或简单统计,而现代数据挖掘技术为我们提供了更科学的解决方案。本文将…...
ESP32多任务水位监测:从Arduino到ESP-IDF的FreeRTOS实战
1. 项目概述:从Arduino到ESP-IDF的跃迁去年我在做毕业设计时,为了搭建一个ESP32的传感器节点演示程序,第一次深入使用了FreeRTOS。那段时间,我几乎天天和任务调度、队列、信号量打交道,从最初的一头雾水到后来能流畅地…...
DragonBones与Godot集成:骨骼动画的可编程化实践
1. 为什么在Godot里用DragonBones不是“锦上添花”,而是“绕不开的刚需” 去年上线一个横版动作手游Demo时,美术团队交来一套20个角色、每个角色含8套动画(待机/跑动/跳跃/攻击/受击/死亡/闪避/必杀)的Spine资源。我兴冲冲导入God…...
Python UiAutomation实战:从网页数据抓取到桌面应用,一个库打通数据采集全链路
Python UiAutomation实战:打通数据采集全链路的智能解决方案 在数据驱动的商业环境中,企业常常面临跨平台数据采集的挑战——财务系统里的交易记录需要与网站后台的报表进行交叉分析,销售数据要从桌面软件导出后上传到云端处理系统。传统的人…...
在多轮对话应用中观察Taotoken计费对成本的影响
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在多轮对话应用中观察Taotoken计费对成本的影响 效果展示类,结合一个需要维护长上下文的多轮对话应用案例,…...
告别元素变动导致的报错:探索自动化测试脚本的 AI“自愈”能力
前言:一个所有测试人都经历过的噩梦 周三晚上十一点,CI/CD流水线再次亮起红灯。 你打开日志,满屏的NoSuchElementException扑面而来。仔细一看——前端团队在昨天的版本中重构了登录页面的DOM结构,原本的#login-btn变成了#signin-button-v2,30个测试用例因此全军覆没。 …...
收藏干货|2026 版企业 AI 落地实操指南,程序员小白入门避坑必备
如今人工智能早已脱离概念炒作阶段,全面扎根企业实际业务场景,成为技术从业者与企业管理者无法回避的发展课题。各行各业都加速布局AI赛道,行业心态也从初期观望试探,彻底转变为实打实的落地攻坚。 不少企业高层主动牵头统筹AI规划…...
如何在浏览器中一键解密所有加密音乐文件:Unlock-Music完全指南
如何在浏览器中一键解密所有加密音乐文件:Unlock-Music完全指南 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地…...
BetterNCM安装器终极指南:5分钟解锁网易云音乐无限潜能
BetterNCM安装器终极指南:5分钟解锁网易云音乐无限潜能 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 你是否觉得网易云音乐PC版功能有限,界面单调?…...
