Redis系列之客户端Redisson
概述
官方推荐的客户端,支持Redis单实例、Redis哨兵、Redis Cluster、Redis master-slave等各种部署架构。
GitHub,
功能:
- 分布式锁
分布式锁
使用Redisson提供的分布式锁的一个最常见场景,应用部署为多个节点,然后使用Spring提供的原生@Scheduled任务调度功能;而没有使用xxl-job等轻量级分布式任务调度系统(底层基于数据库悲观锁)
@Scheduled(cron = "0 0 8 * * ?")
public void execute() {RLock lock = redissonClient.getLock("myLock");try {boolean isLock = lock.tryLock(1, 5, TimeUnit.MINUTES);if (!isLock) {log.warn("job正在执行!");return;}log.info("任务开始执行!");} catch (Exception e) {log.error("执行失败:", e);lock.unlock();}
}
通过lock.tryLock()
查看源码,一步步往里看:
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {this.internalLockLeaseTime = unit.toMillis(leaseTime);return this.commandExecutor.evalWriteAsync(this.getName(), LongCodec.INSTANCE, command, "if (redis.call('exists', KEYS[1]) == 0) then redis.call('hset', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; return redis.call('pttl', KEYS[1]);", Collections.singletonList(this.getName()), new Object[]{this.internalLockLeaseTime, this.getLockName(threadId)});
}
稍微格式化一下,方便阅读:
if (redis.call('exists', KEYS[1]) == 0) then redis.call('hset', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)then redis.call('hincrby', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
return redis.call('pttl', KEYS[1]);
也就是说,需要执行一段Lua脚本:
KEYS[1]
代表加锁的Key,即myLock
ARGV[1]
代表加锁Key的生存时间,默认30秒ARGV[2]
代表加锁客户端ID,格式UUID:n
,如:e197fb92-deeb-4f9d-9d34-51b9b09f0bd7:1
,其中n表示Redis Cluster集群节点
第一段if判断语句,用exists myLock
判断一下,如果要加锁的Key不存在,则通过命令hset myLock e197fb92-deeb-4f9d-9d34-51b9b09f0bd7:1 1
加锁,即设置一个Hash数据结构。命令执行后会生成类似如下数据结构:
myLock:
{
"e197fb92-deeb-4f9d-9d34-51b9b09f0bd7:1": 1
}
接着执行pexpiremyLock 30000
命令,设置myLock这个锁Key的生存时间是30秒,加锁完成。
watch dog自动延期机制
客户端1加锁的Key默认过期时间30秒,客户端1只要加锁成功,就会启动一个watchdog后台线程,每隔10秒检查一下,如果客户端1还持有锁Key,就会不断的延长锁Key的生存时间。
释放锁
执行lock.unlock()
,即释放分布式锁,执行一次lock.unlock()
,对myLock数据结构中的加锁次数减1。
加锁次数未0,说明此客户端已经不再持有锁,触发删除所del myLock
命令。其他客户端即可尝试加锁。
缺点
上面那种方案最大的问题,就是如果你对某个Redis Master实例,写入myLock这种锁Key的Value,此时会异步复制给对应的Master Slave实例。
但是这个过程中一旦发生Redis Master宕机,主备切换,Redis Slave变为Redis Master。
会导致客户端2尝试加锁时,在新的Redis Master上完成加锁,客户端1也以为自己成功加锁。
此时就会导致多个客户端对一个分布式锁完成加锁。这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。
所以这个就是Redis Cluster,或是redis master-slave架构的主从异步复制导致的Redis分布式锁的最大缺陷:在Redis Master实例宕机的时候,可能导致多个客户端同时完成加锁。
在基于NIO的Netty框架上,充分利用Redis提供的一系列优势,
问题
ClassNotFoundException: org.nustaq.serialization.FSTConfiguration
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.redisson.api.RedissonClient]: Factory method 'redisson' threw exception; nested exception is java.lang.NoClassDefFoundError: Lorg/nustaq/serialization/FSTConfiguration;
Caused by: java.lang.ClassNotFoundException: org.nustaq.serialization.FSTConfiguration
解决方案,pom.xml
文件里新增:
<dependency><groupId>de.ruedigermoeller</groupId><artifactId>fst</artifactId><version>2.57</version>
</dependency>
attempt to unlock lock, not locked by current thread by node id
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 633dfc8a-b388-4ba1-ad64-75b491d0c5f2 thread-id: 118at org.redisson.misc.RedissonPromise.trySuccess(RedissonPromise.java:88)at org.redisson.command.CommandAsyncService.handleReference(CommandAsyncService.java:1067)at org.redisson.command.CommandAsyncService.handleSuccess(CommandAsyncService.java:1059)at org.redisson.command.CommandAsyncService.checkAttemptFuture(CommandAsyncService.java:1041)at org.redisson.command.CommandAsyncService$12.operationComplete(CommandAsyncService.java:805)at org.redisson.misc.RedissonPromise.trySuccess(RedissonPromise.java:88)at org.redisson.client.handler.CommandDecoder.completeResponse(CommandDecoder.java:448)at org.redisson.client.handler.CommandDecoder.handleResult(CommandDecoder.java:443)at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:354)at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:128)at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:108)
解决方案:
finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}
}
Command (SET), params [] succesfully sent, but channel [] has been closed
详细的报错信息:
org.springframework.data.redis.RedisConnectionFailureException: Command (SET), params [] succesfully sent, but channel [] has been closed!
at org.redisson.spring.data.connection.RedissonExceptionConverter.convert(RedissonExceptionConverter.java:40)at org.redisson.spring.data.connection.RedissonExceptionConverter.convert(RedissonExceptionConverter.java:35)at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)at org.redisson.spring.data.connection.RedissonConnection.transform(RedissonConnection.java:237)at org.redisson.spring.data.connection.RedissonConnection.syncFuture(RedissonConnection.java:232)at org.redisson.spring.data.connection.RedissonConnection.sync(RedissonConnection.java:462)at org.redisson.spring.data.connection.RedissonConnection.write(RedissonConnection.java:828)at org.redisson.spring.data.connection.RedissonConnection.set(RedissonConnection.java:596)at org.springframework.data.redis.connection.DefaultStringRedisConnection.set(DefaultStringRedisConnection.java:946)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95)at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:236)
参考GitHub-issue:
Redis connection is closed for some reason. Try to set pingConnectionInterval: 60000.
解决方法:在redisson.yml
文件里新增配置:pingConnectionInterval: 60000
RedisResponseTimeoutException: Redis server response timeout occured after 3 retry attempts. Command: params:
解决方法同上,新增配置。
参考
相关文章:
Redis系列之客户端Redisson
概述 官方推荐的客户端,支持Redis单实例、Redis哨兵、Redis Cluster、Redis master-slave等各种部署架构。 GitHub, 功能: 分布式锁 分布式锁 使用Redisson提供的分布式锁的一个最常见场景,应用部署为多个节点,然…...

centos 端口被占用的快速排查方式
问题笔记 centos 端口被占用的快速排查方式 centos 端口被占用的快速排查方式 这里说一个我刚刚遇到的问题,解决步骤用来记录,方便以后自己查询。 nginx配置完index.html测试文件,发现一直显示的404页面。 我跑到服务器上想重启一下nginx …...

Java“牵手”淘宝商品列表数据,关键词搜索淘宝商品数据接口,淘宝API申请指南
淘宝商城是一个网上购物平台,售卖各类商品,包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取淘宝商品列表和商品详情页面数据,您可以通过开放平台的接口或者直接访问淘宝商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…...
OpenEuler/CentOS如何修改密码策略
密码策略文件: /etc/pam.d/system-auth 找到行: password requisite pam_pwquality.so try_first_pass local_users_only 为保证安全,可以将这一行注释掉,添加一行,最后结果如下: #password …...

# Spring MVC与RESTful API:如何设计高效的Web接口
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...

Scrum敏捷模式的优势点、实践经验及适用企业
Scrum敏捷模式是一种灵活、适应性强的开发方法,其核心理念是以短周期、高频率的方式进行项目开发,确保团队能够快速响应变化。 Scrum包含三个角色:产品负责人(Product Owner)、Scrum Master和开发团队(Tea…...

【C++杂货铺】探索stack和queue的底层实现
文章目录 一、stack的介绍和使用1.1 stack的介绍1.2 stack的使用1.2.1 最小栈1.2.2 栈的压入、弹出序列1.2.3 逆波兰表达式求值1.2.4 用栈实现队列 二、queue的介绍和使用2.1 queue的介绍2.2 queue的使用2.2.1 二叉树的层序遍历 三、模拟实现3.1 stack模拟实现3.2 queue模拟实现…...

“系统的UI”——SystemUI
SystemUI的实现 以StatusBar为例,来分析下Android系统具体是如何实现它们的。 相关代码分为两部分,即: Service部分 代码路径:frameworks/base/services/java/com/android/server。 应用部分 代码路径:frameworks…...

类和对象:构造函数,析构函数与拷贝构造函数
1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器…...
谈谈Java的特点和优点以及选择Java的原因
 如果面试官问你:请你说说Java的特点和优点,为什么要选择Java?你该怎么回答? 得分点 Java的特点 Java与C的区别 Java的优点标准回答 Java是一门非常纯粹的面向对象的编程语言,它吸收了C语言的各种优…...
消息队列(MQ)面试
目录 讲一讲MQ 面试官: 在你之前的项目中,你是否使用过消息队列(MQ)?能详细介绍一下你在项目中如何使用MQ吗? 在用户和用户之间的多对多聊天通信中如何使用,请具体来讲一下。 那你可以讲一下消息的确认…...

无涯教程-JavaScript - COUPNUM函数
描述 COUPNUM函数返回结算日和到期日之间应付的息票数量,四舍五入到最接近的整数。 语法 COUPNUM (settlement, maturity, frequency, [basis])争论 Argument描述Required/OptionalSettlement 证券的结算日期。 证券结算日期是指在发行日期之后将证券交易给买方的日期。 Re…...

上海控安携汽车网络安全新研产品出席AUTOSEMO“恒以致远,共创共赢”主题研讨会
8月31日,AUTOSEMO“恒以致远,共创共赢”主题研讨会在天津成功召开。本次大会由中国汽车工业协会软件分会中国汽车基础软件生态标委会(简称:AUTOSEMO)与天津市西青区人民政府联合主办。现场汇聚了100余位来自产学研政企…...

小程序引入高德/百度地图坐标系详解
小程序引入高德/百度地图坐标系详解 官网最近更新时间:最后更新时间: 2021年08月17日 高德官网之在原生小程序中使用的常见问题 链接 目前在小程序中使用 高德地图只支持以下功能 :地址描述、POI和实时天气数据 小结:从高德api中获取数…...

英诺森 “供应链智能数据平台”荣获“科技进步奖”
近日,2023年中国物流与采购联合会科学技术奖正式公布,该奖项经国家科技部批准,在国家科学技术奖励工作办公室登记备案,是我国物流行业最具影响力的奖项之一。 英诺森联合客户申报的科技项目“英诺森供应链智能数据平台”…...
kafka 3.5 主题分区的Follower创建Fetcher线程从Leader拉取数据源码
Kakfa集群有主题,每一个主题下又有很多分区,为了保证防止丢失数据,在分区下分Leader副本和Follower副本,而kafka的某个分区的Leader和Follower数据如何同步呢?下面就是讲解的这个 首先要知道,Follower的数据…...
Golang web 项目中实现自定义 recovery 中间件
为什么需要实现自定义 recovery 中间件? 在 Golang 的 Web 项目中,自定义 recovery 中间件是一种常见的做法,用于捕获并处理应用程序的运行时错误,以避免整个应用程序崩溃并返回对应格式的响应数据。 很多三方 web 框架…...

Direct3D绘制旋转立方体例程
初始化文件见Direct3D的初始化_direct3dcreate9_寂寂寂寂寂蝶丶的博客-CSDN博客 D3DPractice.cpp #include <windows.h> #include "d3dUtility.h" #include <d3dx9math.h>IDirect3DDevice9* Device NULL; IDirect3DVertexBuffer9* VB NULL; IDirect3…...
ElementUI浅尝辄止31:Tabs 标签页
选项卡组件:分隔内容上有关联但属于不同类别的数据集合。 常见于网站内容信息分类或app内容信息tab分类 1.如何使用? Tabs 组件提供了选项卡功能,默认选中第一个标签页,你也可以通过 value 属性来指定当前选中的标签页。 <temp…...

将 ChatGPT 用于数据科学项目的指南
推荐:使用 NSDT场景编辑器 快速搭建3D应用场景 我们都知道 ChatGPT 的受欢迎程度以及人们如何使用它来提高生产力。但是,如果您是新手,则值得注册ChatGPT免费演示并尝试它所能做的一切。您还应该参加我们的 ChatGPT 简介课程,学习…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...