Redisson框架

1. Redisson锁与Redis订阅与发布模式的联系:

Redisson锁中,使用订阅发布模式去通知等待锁的客户端:锁已经释放,可以进行抢锁。

  • publish channel_name message:将消息发送到指定频道
    • 解锁时,在Lua解锁脚本中使用:场景1:锁不存在时,发送消息到解锁频道;场景2:锁重入记数hash等于0时,发送消息到解锁频道。
  • subscribe channel_name ... :客户端订阅一个或多个频道消息
    • 加锁时,没有争抢锁成功的客户端,需要订阅解锁频道 -> RedissonLock#tryLock中订阅频道后,会用await()阻塞等待锁释放。直到获取推送的频道解锁消息,才取消等待。

  • unsubsribe channel_name ... :客户端退订一个或多个频道消息
    • 加锁时,客户端最后都会去退订解锁频道,不管加锁成功与失败。

2. RedissonLock锁:

数据结构:

  • Hash结构:key -> name 、field -> lockName (id+":"+threadId -> UUID:threadId) 、 value -> 可重入次数

加锁伪脚本:

// 如果不存在锁:则新增锁,并设置锁重入计数为1、设置锁过期时间,返回nil结束
exists name == 0                      ->  name:加锁的key名称
hset name lockName  1                 -> lockName:当前客户端标识
pexpire name  internalLockLeaseTime   -> internalLockLeaseTime:锁租期
return nil// 如果存在锁,且唯一标识也匹配,表示当前锁可重入,重入计数+1,并重新设置锁过期时间,返回nil结束
hexists  name lockName = 1
hincrby  name lockName  1 
pexpire name  internalLockLeaseTime 
return nil// 如果存在锁,且唯一标识不匹配,表示锁是被其他线程占用,返回剩余时间结束
return pttl name

解锁伪脚本:

// 如果锁不存在:则直接广播解锁消息,返回1,结束
exists name == 0 
publish channelName  0   -> channelName:频道名称 -> redisson_lock__channel:{name}
return 1// 如果锁存在,但是但唯一标识不匹配:表示锁被其他线程占用,当前线程不允许解锁,返回nil,结束
hexists name lockName == 0 
return nil// 如果锁存在,并且唯一标识匹配:则先将锁重入计数 -1
counter = hincrby name lockName -1
// 如果锁重入计数-1后,还大于0,重新设置有效期,返回0,结束
pexpire name  internalLockLeaseTime
return 0// 如果锁重入计数-1后,等于0,直接删除key,并广播解锁消息(即唤醒其它争抢锁的被阻塞的线程),返回1,结束
del name 
publish channelName  0
return 1

参考文档:https://www.cnblogs.com/huangwentian/p/14622441.html

3. RedissonFairLock锁:

数据结构:

  • Hash结构:key -> name 、field -> lockName (UUID:threadId)、 value -> 可重入次数
  • List结构:key -> redisson_lock_queue:{lockName} 、vaule -> lockName (UUID:threadId)
    • 作用:实现公平锁,按顺序记录线程争抢锁的顺序。
  • Zset结构:key -> redisson_lock_timeout:{lockName}、 score -> timeout(过期时间,它是时间戳) member -> lockName (id+":"+threadId)
    • 作用1:利用Zset分值的功能,清除等待队列中,等待加锁,但是已经过期的线程。
    • 作用2:利用Zset分值的功能,在加锁不成功后,计算需要等待的时间(ttl)和更新Zset分值(timeout)

加锁伪脚本:

/ 删除列表中第一个位置是过期的线程
// 死循环
while true do // list中获取第一元素,如果firstThreadId2为空,直接结束循环firstThreadId2 = lindex  redisson_lock_queue:{lockName}  0if firstThreadId2 == falsebreak;// 如果firstThreadId2不为空,从Zset中获取分值timeouttimeout = zscore redisson_lock_timeout:{lockName} firstThreadId2// 如果 timeout <= 当前时间戳,从Zset中删除该成员,从List弹出该元素if timeout <= currentTimezrem redisson_lock_timeout:{lockName}  firstThreadId2lpop  redisson_lock_queue:{lockName} // 如果 timeout > 当前时间戳,结束循环break;// 如果不存在name的key,并且不存队列或者队列第一个元素是lockName时,当前线程可以获得锁
if exists name == 0  && (exists redisson_lock_timeout:{lockName} == 0  ||   lindex  redisson_lock_queue:{lockName}  0 ==  lockName)  lpop redisson_lock_queue:{lockName}  zrem redisson_lock_timeout:{lockName}  lockNamehset name  lockName  1pexpire name internalLockLeaseTimereturn nil// 如果存在name的key,并且hash结构的field也是lockname时,表示当前线程已经获得锁,此时处理锁重入
if hexists name lockName == 1hincrby name lockName 1pexpire name internalLockLeaseTimereturn nil//  获取列表第一个元素,计算当前的ttl,然后再重新计算timeout赋值给Zset中,Zset添加成功后,再rpush到队列,返回ttl
firstThreadId = lindex  redisson_lock_queue:{lockName}  0
ttl
// 如果列表第一元素不为空,并且不是lockName时,从Zset中获取lockName的timeout,计算ttl
if firstThreadId ~= false and firstThreadId ~= lockNamettl = (zscore redisson_lock_timeout:{lockName} lockName) - currentTime// 列表第一元素是lockName时,获取ttlttl = pttl name// 计算 timeouttimeout = ttl + (currentTime + threadWaitTime);// 如果 zadd 添加成功,则 rpushif (zadd  redisson_lock_timeout:{lockName}  timeout lockName) == 1rpush redisson_lock_queue:{lockName}  return ttl          

解锁伪脚本:

// 删除列表中第一个位置是过期的线程
... ...
// 1. 如果不存name时,获取列表第一个元素,并且第一元素不为空,向解锁频道发送通知
if exists name == 0nextThreadId = lindex  redisson_lock_queue:{lockName}  0if nextThreadId ~= false publish channel_name .. ':' .. nextThreadId 0return 1// 2. 如果 hexists name lockName 不存在时,直接返回nil
if hexists name lockName == 0return nil// 3. 如果 hexists name lockName 存在时,处理锁可重入,或删除key,通知下一个元素
counter = hincrby  name  lockName  -1
// 如果counter大于0,设置过期时间
if counter > 0pexpire name internalLockLeaseTime
return 0
// 如果counter小于等于0,删除key
del name
// 获取列表第一个元素,如果不为空,发送解锁通知
nextThreadId = lindex  redisson_lock_queue:{lockName}  0
if nextThreadId ~= false publish channel_name .. ':' .. nextThreadId 0
return 1   

4. 延迟队列:

两个API概念:

  • RBlockingQueue 就是目标队列
  • RDelayedQueue 就是中转队列

  • 我们实际操作的是RBlockingQueue队列,并不是RDelayedQueue队列,RDelayedQueue主要是提供中间转发的一个队列。
  • 我们都是基于RBlockingQueue目标队列在进行消费,而RDelayedQueue就是会把过期的消息放入到我们的目标队列中。
  • 我们只要从RBlockingQueue队列中取数据即可。

数据结构:

  • ZSet结构:key -> redisson_delay_queue_timeout:{name}、score -> System.currentTimeMillis() + delayInMs、member ->数据使用 struct.pack()包装,包含 'dLc0'、randomId、string.len(数据)、数据
    • 作用:该队列实现了获取延迟数据的功能。
  • List结构:key-> redisson_delay_queue:{name} 、value -> 数据使用 struct.pack()包装,包含 'dLc0'、randomId、string.len(数据)、数据
    • 作用:该队列记录了所有数据的加入顺序。
  • List结构:key -> 出入阻塞队列的名称,上边的{name}、value -> 数据

处理流程(展示一条主线):

Redisson延迟队列剖析_why redissondelayedqueue use list-CSDN博客

  • 实例化RedissonDelayedQueue时:
  1. 设置频道名称(redisson_delay_queue_channel:{name})、队列名称(redisson_delay_queue:{name})、延迟队列名称(redisson_delay_queue_timeout:{name})
  2. 设一个“将到期的任务推送阻塞队列”的任务(i.获取到期数据,到阻塞队列的Lua脚本;ii.设置发布订阅的主题)
  3. 启动该任务,并且添加相关监听(i.添加监听订阅发布的事件 ->pushTask() ;ii.添加监听消息的事件->scheduleTask(startTime))
  • 添加消息时:
  1. 向timeoutSet延迟队列添加数据
  2. 向queue队列添加数据
  3. 获取timeoutSet延迟队列的队头数据,如果队头数据等于刚加入的数据,则向频道发送消息
  • 触发频道监听事件->scheduleTask(startTime):
  1. 计算延迟时间 -> delay = startTime - System.currentTimeMillis()
  2. 如果延迟大于10,启动延迟任务
  3. 否则立即执行启动 -> pushTask()
  • 将到期的任务推送到阻塞队列 -> pushTask():
  1. 调用实例化时,定义的pushTaskAsync
  2. 添加监听操作完成的事件,在执行成功后,判断返回值是否为空,不为空调用scheduleTask(future.getNow())

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法思想总结:优先级队列

一、最后一块石头的重量 . - 力扣&#xff08;LeetCode&#xff09; 我们每次都要快速找到前两个最大的石头进行抵消&#xff0c;这个时候用优先级队列&#xff08;建大堆&#xff09;,不断取堆顶元素是最好的&#xff01;每次删除堆顶元素后&#xff0c;可以自动调整&#xf…

[C++][CMake][CMake基础]详细讲解

目录 1.CMake简介2.大小写&#xff1f;3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具&#xff0c;并且是跨平台的 问题 – 解决 如果自己动手写Makefile&#xff0c;会发现&#xff0c;Makefile通常依赖于当前的编译平台&#xff0c;而且编写Makefile的…

【动态规划】动态规划一

动态规划一 1.第 N 个泰波那契数2.面试题 08.01. 三步问题3.使用最小花费爬楼梯4.解码方法 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.…

分布式限流:Spring Cloud Gateway 限流

分布式限流&#xff1a;Spring Cloud Gateway 限流 在现代微服务架构中&#xff0c;流量控制是一个至关重要的部分。分布式限流作为一种有效的流量控制手段&#xff0c;能够帮助我们保护系统不被突发的流量冲垮。Spring Cloud Gateway支持多种限流方式。 什么是分布式限流 分…

模拟5亿年自然进化史,全新蛋白质大模型ESM3诞生!前Meta老将力作LeCun转赞

模拟5亿年自然进化史&#xff0c;全新蛋白质大模型ESM3诞生&#xff01;前Meta老将力作LeCun转赞。 能抗衡AlphaFold 3的生命科学大模型终于出现了。初创公司Evolutionary Scale AI发布了他们最新的98B参数蛋白质语言模型ESM3。不仅支持序列、结构、功能的all-to-all推理&#…

Unreal Engine@Jetson Orin Nano尚不支持

Unreal EngineJetson Orin Nano尚不支持 1. 源由2. Unreal Engine介绍3. 问题4. 编译方法5. 补充6. 其他 1. 源由 最近在看SC-Explorer方面的内容&#xff0c;在模拟方面采用了Unreal Engine。 本打算跑下模拟&#xff0c;因此打算在JetsonOrin的板子上试试看。 2. Unreal En…

opencv实现目标检测功能----20240704

早在 2017 年 8 月,OpenCV 3.3 正式发布,带来了高度改进的“深度神经网络”(dnn)模块。 该模块支持多种深度学习框架,包括 Caffe、TensorFlow 和 Torch/PyTorch。这次我们使用Opencv深度学习的功能实现目标检测的功能,模型选用MobileNetSSD_deploy.caffemodel。 模型加载…

mac视频压缩简单办法,mac如何把视频压缩到指定大小内存

在数字时代&#xff0c;视频已成为我们日常生活和工作的重要交流工具。然而&#xff0c;视频文件体积庞大&#xff0c;给存储和分享带来了不少困扰。本文将为你揭秘视频压缩的秘密&#xff0c;让你轻松减小视频文件体积&#xff0c;提升分享效率&#xff01; 方法一下载文件压缩…

Python爬虫教程第0篇-写在前面

为什么写这个系列 最近开发了个Python爬虫的脚本&#xff0c;去抢一个名额&#xff0c;结果是程序失败了&#xff0c;中间有各种原因&#xff0c;终究还是准备不足的问题。我想失败的经验或许也可贵&#xff0c;便总结一下当初从0开始学Python&#xff0c;一步步去写Python脚本…

【SpringCloud】Ribbon源码解析

ribbon是一个负载均衡组件&#xff0c;它可以将请求分散到多个服务提供者实例中&#xff0c;提高系统的性能和可用性。本章分析ribbon是如何实现负载均衡的 1、LoadBalanced 消费者在引入ribbon组件后&#xff0c;给http客户端添加LoadBalanced注解就可以启用负载均衡功能。Lo…

Github 2024-07-01开源项目月报 Top15

根据Github Trendings的统计,本月(2024-07-01统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6JavaScript项目3C++项目2PHP项目1Blade项目1非开发语言项目1C#项目1Lua项目1Go项目1MDX项目1Jupyter Notebook项目1从零开始构建你喜…

Ubuntu 24.04-自动安装-Nvidia驱动

教程 但在安全启动模式下可能会报错。 先在Nvidia官网找到GPU对应的驱动版&#xff0c; 1. 在软件与更新中选择合适的驱动 2. ubuntu自动安装驱动 sudo ubuntu-drivers autoinstall显示驱动 ubuntu-drivers devices3. 安装你想要的驱动 sudo apt install nvidia-driver-ve…

99. 岛屿数量

题目描述&#xff1a;给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成&#xff0c;并且四周都是水域。你可以假设矩阵外均被水包围。 输入描述&#xff1a…

@amap/amap-jsapi-loader实现高德地图嵌入React项目中,并且做到点击地图任意一处,获得它的经纬度

1.第一步要加入项目package.json中或者直接yarn install它都可以 想必大家应该都会 "amap/amap-jsapi-loader": "0.0.7"2.加入项目中 关于接口获取key的接口 大家改成自己对应的项目请求方法 import React, { PureComponent } from react; import { Input…

GEE计算遥感生态指数RSEI

目录 RESI湿度绿度热度干度源代码归一化函数代码解释整体的代码功能解释:导出RSEI计算结果参考文献RESI RSEI = f (Greenness,Wetness,Heat,Dryness)其遥感定义为: RSEI = f (VI,Wet,LST,SI)式中:Greenness 为绿度;Wetness 为湿度;Thermal为热度;Dryness 为干度;VI 为植被指数…

【CT】LeetCode手撕—4. 寻找两个正序数组的中位数

目录 题目1- 思路2- 实现⭐4. 寻找两个正序数组的中位数——题解思路 3- ACM 实现 题目 原题连接&#xff1a;4. 寻找两个正序数组的中位数 1- 思路 思路 将寻找中位数 ——> 寻找两个合并数组的第 K 大 &#xff08;K代表中位数&#xff09; 实现 ① 遍历两个数组 &am…

如何利用AI撰写短文案获客?分享6大平台和3大步骤!

从去年开始&#xff0c;很多大厂都在裁员&#xff0c;原因就是因为AI的火爆&#xff0c;替代了很多机械式的劳动力。以前很多人可以通过机械式的工作来摸鱼&#xff0c;现在AI完成的效率比人工的要高很多倍。 国内好用的AI平台非常多&#xff0c;有时候也可以使用几个AI平台结合…

如何提高内容生产效率

在当前数字化和信息爆炸的时代&#xff0c;内容生产的需求不断增加&#xff0c;如何提高内容生产效率成为企业面临的重要课题。本文将介绍一种有效的内容生产协同工具——【可瓜】&#xff0c;并详细探讨其在提高内容生产效率方面的优势和实际应用。 内容生产任务进度追踪 传统…

【BUUCTF-PWN】7-[第五空间2019 决赛]PWN5

参考&#xff1a;BUU pwn [第五空间2019 决赛]PWN5 //格式化字符串漏洞 - Nemuzuki - 博客园 (cnblogs.com) 格式化字符串漏洞原理详解_printf 任意内存读取-CSDN博客 32位小端排序&#xff0c;有栈溢出保护 运行效果&#xff1a; 查看main函数 存在格式化字符串漏洞 输…

基于iview.viewUI实现行合并(无限制/有限制合并)【已验证可正常运行】

1.基于iview.viewUI实现行合并&#xff08;列之间没有所属对应关系&#xff0c;正常合并&#xff09; 注&#xff1a;以下代码来自于GPT4o&#xff1a;国内直连GPT4o 只需要修改以下要合并的列字段&#xff0c;就可以方便使用啦 mergeFields: [majorNo, devNam, overhaulAdvic…