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,即myLockARGV[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:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
麒麟系统使用-进行.NET开发
文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的,如果需要进行.NET开发,则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET,所以要进…...
Linux入门(十五)安装java安装tomcat安装dotnet安装mysql
安装java yum install java-17-openjdk-devel查找安装地址 update-alternatives --config java设置环境变量 vi /etc/profile #在文档后面追加 JAVA_HOME"通过查找安装地址命令显示的路径" #注意一定要加$PATH不然路径就只剩下新加的路径了,系统很多命…...
npm安装electron下载太慢,导致报错
npm安装electron下载太慢,导致报错 背景 想学习electron框架做个桌面应用,卡在了安装依赖(无语了)。。。一开始以为node版本或者npm版本太低问题,调整版本后还是报错。偶尔执行install命令后,可以开始下载…...
