springboot中加分布式redis锁
分布式redis锁,spring-boot-starter-data-redis,RedisTemplate
公司聊天的聊天系统,近期出现多个客服并发接待同一个客户的记录,经排查,是由于代码加的同步锁在集群环境下不适用,
我们的客服系统是2条服务器,redis中缓存session,故考虑通过加redis分布式锁来解决该问题.
根据实际情况,只针对单台redis实例,代码逻辑先获取锁再执行操作.
代码体系用的是spring-boot-starter-data-redis,所以访问redis的客户端就是 RedisTemplate<String, String> redisTemplate
具体的加锁和解锁方法,用到了lua脚本来操作,尽可能的保证操作的原子性.
脚本封装入 DefaultRedisScript对象中,再使用RedisTemplate来执行,官方介绍
6.11. Redis Scripting
https://docs.spring.io/spring-data/redis/docs/1.6.2.RELEASE/reference/html/
lua脚本操作redis相关命令理解,请参考 http://redisdoc.com/string/index.html
1,获取锁lua脚本
redis.call('set', KEYS[1], ARGV[1],ARGV[2],ARGV[3],ARGV[4]) if redis.call('get',KEYS[1]) == ARGV[1] then return true else return false end
参数对应 set( key,reqId,NX,PX,时间毫秒)
原子操作 set NX参数,不存在key则设置key-value, PX 并同时设置key超时毫秒数,这里为了返回参数判断,加入get 获取key的值是否等于我设置的值,是则获取锁成功.
注意 expireTime 是 int类型的,RedisTemplate 底层要转字符串的,所以入参是记得加 String.valueOf(expireTime),否则会出现转换异常,当然只是我在处理得时候遇到了,特别说下.
当然这是我的参考实现,要有更好建议,还请提出.
2,释放锁lua脚本
if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return true else return false end
参数对应 get( key) == reqId 是true则直接删除key释放锁
这个实现参考了另一篇博客,那个是用Jedis做客户端而我在RedisTemplate底层调用中没找到相似功能的方法,所以才改变思路都用lua脚本操作,后面附博客链接。
3,贴上代码如下
package com.xxxxx.kefu.utils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.Collections;
public class RedisLockTool {
/**不存在则设置k - v 值*/
private static final String SET_IF_NOT_EXIST = "NX";
/**设置毫秒超时*/
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 尝试获取分布式锁
*/
public static boolean getLock(RedisTemplate<String, String> redisTemplate, String lockKey, String requestId, int expireTime) {
String script = "redis.call('set', KEYS[1], ARGV[1],ARGV[2],ARGV[3],ARGV[4]) if redis.call('get',KEYS[1]) == ARGV[1] then return true else return false end";
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>(script,Boolean.class);
Boolean result = redisTemplate.execute(redisScript,Collections.singletonList(lockKey), requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,String.valueOf(expireTime));
if (result != null && result) {
return true;
}
return false;
}
/**
* 释放分布式锁
*/
public static boolean unLock(RedisTemplate<String, String> redisTemplate, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return true else return false end";
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>(script,Boolean.class);
Boolean ok = redisTemplate.execute(redisScript,Collections.singletonList(lockKey),requestId);
if(ok != null && ok){
return true;
}else{
return false;
}
}
}
4,参考博客
https://yq.aliyun.com/articles/307547
里面指出了,其他实现方法的一些问题,在某些情况下不能确保操作原子性,看了理解之后,我发现同事之前用的redis锁在删除锁时没有判断客户端id,也就是里面指出的错误示例,极端情况下会出现。
参考改造出了这个实现,个人觉得是没有什么大问题的,如有发现不妥之处,还请指教.
5,查阅的其他链接
https://docs.spring.io/spring-data/redis/docs/1.6.2.RELEASE/reference/html/ RedisTemplate调用lua脚本示例
http://redisdoc.com/string/index.html lua脚本操作redis 示例
springboot中加分布式redis锁的更多相关文章
- springboot中配置主从redis
测试redis的主从配置 redis实例 文件夹名称如下 redis_master_s redis_slaver1_s redis_slaver2_s redis.conf文件 master的redi ...
- spring-boot 中实现标准 redis 分布式锁
一,前言 redis 现在已经成为系统缓存的必备组件,针对缓存读取更新操作,通常我们希望当缓存过期之后能够只有一个请求去更新缓存,其它请求依然使用旧的数据.这就需要用到锁,因为应用服务多数以集群方式部 ...
- redis(Springboot中封装整合redis,java程序如何操作redis的5种基本数据类型)
平常测试redis操作命令,可能用的是cmd窗口 操作redis,记录一下 java程序操作reids, 操作redis的方法 可以用Jedis ,在springboot 提供了两种 方法操作 Red ...
- SpringBoot中加载XML配置
开篇 在SpringBoot中我们通常都是基于注解来开发的,实话说其实这个功能比较鸡肋,但是,SpringBoot中还是能做到的.所以用不用是一回事,会不会又是另外一回事. 涛锅锅在个人能力能掌握的范 ...
- springboot中如何向redis缓存中存入数据
package com.hope;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jack ...
- redis分布式锁扣减库存弊端: 吞吐量低, 解决方法:使用 分段锁 分布式分段锁并发扣减库存--代码实现
package tech.codestory.zookeeper.aalvcai.ConcurrentHashMapLock; import lombok.AllArgsConstructor; im ...
- [SpringBoot]SpringBoot中使用redis事务
本文基于SpringBoot 2.X 事务在关系型数据库的开发中经常用到,其实非关系型数据库,比如redis也有对事务的支持,本文主要探讨在SpringBoot中如何使用redis事务. 事务的相关介 ...
- 定时任务redis锁+自定义lambda优化提取冗余代码
功能介绍: 我系统中需要跑三个定时任务,由于是多节点部署,为了防止多个节点的定时任务重复执行.所以在定时任务执行时加个锁,抢到锁的节点才能执行定时任务,没有抢到锁的节点就不执行.从而避免了定时任务重复 ...
- redis分布式锁在springboot中的实现
理论知识 redis分布式锁的实现方案请参考文章 如何优雅地用redis实现分布式锁 本案例简介 以秒杀活动为例子,在多线程高并发的情况下需要保证秒杀业务的线程安全性,确保秒杀记录与所扣库存数 ...
随机推荐
- matlab 读写其他格式数据文件(excel)
1. excel matlab和excel 中的数据互相导入 xlswrite() mat ⇒ excel 请问怎么把大容量的mat文件导出到excel文件中 – MATLAB中文论坛 % data. ...
- WinEdt && LaTex(三)—— 宏包
amsmath:最常用的数学宏包 1. bm:bold math 数学字体加粗 \documentclass{article} \usepackage{bm} \begin{document} \[ ...
- 神户制钢坑了500家企业 百年老店为何走上邪路?(企业经营再艰难,也不能降低产品质量,甚至偷工减料,同样适用于IT行业)
神户制钢这颗烂萝卜,拔出它之后带出的泥越来越多.上周五社长川崎博也又开了记者会,再次道歉,而受到其数据造假影响的客户数量也从200家飙升到500家. 日本政府给神户制钢两周时间调查,还要在一个月内公布 ...
- OpenGL(二十二) gluBuild2DMipmaps 加载Mip纹理贴图
当纹理被用于渲染一个面积比它本身小很多的对象时,会由于纹理图像的降采样率不足而导致混叠现象,主要的表现特征是纹理图像的闪烁,出现纹理躁动.特别是在场景远近移动变换时,这种闪烁情况更为明显,严重可能会影 ...
- 微信小程序--实现图片上传
前端: 微信开发者工具 后端:.Net 服务器:阿里云 这里介绍微信小程序如何实现上传图片到自己的服务器上 前端代码 data: { productInfo: {} }, //添加Banner bin ...
- Android Ant 和 Gradle 包装工艺和效率控制
一个.Ant 包:(下载ant.配置环境变量不说) 1.进入命令行模式,并切换到项目文件夹,运行例如以下命令为ADT创建的项目加入ant build支持: android update project ...
- OpenSSL RSA加解密 (.Net公钥加密/ Linux端私钥解密)
要求在.Net端生成公钥私钥对. 然后在.Net端使用RSA公钥加密:在Linux端使用RSA私钥解密. 最初的尝试是:.Net端使用RSACryptoServiceProvider; linux端使 ...
- WPF 4 TextBox 笔刷特效
原文:WPF 4 TextBox 笔刷特效 TextBox 控件是我们开发过程中必不可少的组件,它可以使应用程序方便的与用户进行文字交互.在新WPF 4 中又为TextBox 添加了两种新笔 ...
- 通通WPF随笔(1)——基于lucene.NET让ComboBox拥有强大的下拉联想功能
原文:通通WPF随笔(1)--基于lucene.NET让ComboBox拥有强大的下拉联想功能 我一直很疑惑百度.谷哥搜索框的下拉联想功能是怎么实现的?是不断地查询数据库吗?其实到现在我也不知道,他们 ...
- sql执行分页查询
declare @PageSize int,@PageIndex int;set @PageSize=5;set @PageIndex=2;WITH Pagination AS( SELECT * ...