扩展redisTemplate实现分布式锁
原文:https://blog.csdn.net/qq1010267837/article/details/79697572
依赖jar包
compile group: 'redis.clients', name: 'jedis', version:'2.8.1'
compile group: 'org.springframework.data', name: 'spring-data-redis', version:'1.6.5.RELEASE'
/**
* Redis的分布式锁对象
* Created by zhengjy on 2017/3/6.
*/
public interface RedisLock extends AutoCloseable { /**
* 释放分布式锁
*/
void unlock();
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import redis.clients.jedis.Jedis; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit; /**
* @Resource(name="stringRedisTemplate")
* private RedisAtomicClient redisAtomicClient;
*
* 提供Redis一些不直接支持的原子性的操作,很多实现采用了lua脚本
* Created by zhengjy on 2017/3/6.
*/
public class RedisAtomicClient {
private static final Logger logger = LoggerFactory.getLogger(RedisAtomicClient.class); private final RedisTemplate redisTemplate;
private final StringRedisTemplate stringRedisTemplate; private static final String INCR_BY_WITH_TIMEOUT = "local v;" +
" v = redis.call('incrBy',KEYS[1],ARGV[1]);" +
"if tonumber(v) == 1 then\n" +
" redis.call('expire',KEYS[1],ARGV[2])\n" +
"end\n" +
"return v";
private static final String COMPARE_AND_DELETE =
"if redis.call('get',KEYS[1]) == ARGV[1]\n" +
"then\n" +
" return redis.call('del',KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end"; public RedisAtomicClient(RedisTemplate redisTemplate){
this.redisTemplate = redisTemplate;
this.stringRedisTemplate = new StringRedisTemplate();
this.stringRedisTemplate.setConnectionFactory(redisTemplate.getConnectionFactory());
this.stringRedisTemplate.afterPropertiesSet();
} /**
* 根据key获得对应的long类型数值,不存在则返回null(本方法使用string序列化方式)
* @param key
* @return
*/
public Long getLong(String key){
try {
String val = stringRedisTemplate.opsForValue().get(key); if(val == null){
return null;
}else{
return Long.valueOf(val);
}
} catch(Exception e){
logger.error("get key error:"+key, e);
return null;
}
} /**
* 计数器,支持设置失效时间,如果key不存在,则调用此方法后计数器为1(本方法使用string序列化方式)
* @param key
* @param delta 可以为负数
* @param timeout 缓存失效时间
* @param timeUnit 缓存失效时间的单位
* @return
*/
public Long incrBy(String key, long delta, long timeout, TimeUnit timeUnit){
List<String> keys = new ArrayList<>();
keys.add(key);
long timeoutSeconds = TimeUnit.SECONDS.convert(timeout, timeUnit);
String[] args = new String[2];
args[0] = String.valueOf(delta);
args[1] = String.valueOf(timeoutSeconds);
Object currentVal = stringRedisTemplate.execute(new DefaultRedisScript<>(INCR_BY_WITH_TIMEOUT, String.class), keys, args); if(currentVal instanceof Long){
return (Long)currentVal;
}
return Long.valueOf((String)currentVal);
} /**
* 获取redis的分布式锁,内部实现使用了redis的setnx。只会尝试一次,如果锁定失败返回null,如果锁定成功则返回RedisLock对象,调用方需要调用RedisLock.unlock()方法来释放锁.
* <br/>使用方法:
* <pre>
* RedisLock lock = redisAtomicClient.getLock(key, 2);
* if(lock != null){
* try {
* //lock succeed, do something
* }finally {
* lock.unlock();
* }
* }
* </pre>
* 由于RedisLock实现了AutoCloseable,所以可以使用更简介的使用方法:
* <pre>
* try(RedisLock lock = redisAtomicClient.getLock(key, 2)) {
* if (lock != null) {
* //lock succeed, do something
* }
* }
* </pre>
* @param key 要锁定的key
* @param expireSeconds key的失效时间
* @return 获得的锁对象(如果为null表示获取锁失败),后续可以调用该对象的unlock方法来释放锁.
*/
public RedisLock getLock(final String key, long expireSeconds){
return getLock(key, expireSeconds, 0, 0);
} /**
* 获取redis的分布式锁,内部实现使用了redis的setnx。如果锁定失败返回null,如果锁定成功则返回RedisLock对象,调用方需要调用RedisLock.unlock()方法来释放锁
* <br/>
* <span style="color:red;">此方法在获取失败时会自动重试指定的次数,由于多次等待会阻塞当前线程,请尽量避免使用此方法</span>
*
* @param key 要锁定的key
* @param expireSeconds key的失效时间
* @param maxRetryTimes 最大重试次数,如果获取锁失败,会自动尝试重新获取锁;
* @param retryIntervalTimeMillis 每次重试之前sleep等待的毫秒数
* @return 获得的锁对象(如果为null表示获取锁失败),后续可以调用该对象的unlock方法来释放锁.
*/
public RedisLock getLock(final String key, final long expireSeconds, int maxRetryTimes, long retryIntervalTimeMillis){
final String value = key.hashCode()+""; int maxTimes = maxRetryTimes + 1;
for(int i = 0;i < maxTimes; i++) {
String status = stringRedisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
Jedis jedis = (Jedis) connection.getNativeConnection();
String status = jedis.set(key, value, "nx", "ex", expireSeconds);
return status;
}
});
if ("OK".equals(status)) {//抢到锁
return new RedisLockInner(stringRedisTemplate, key, value);
} if(retryIntervalTimeMillis > 0) {
try {
Thread.sleep(retryIntervalTimeMillis);
} catch (InterruptedException e) {
break;
}
}
if(Thread.currentThread().isInterrupted()){
break;
}
} return null;
} private class RedisLockInner implements RedisLock{
private StringRedisTemplate stringRedisTemplate;
private String key;
private String expectedValue; protected RedisLockInner(StringRedisTemplate stringRedisTemplate, String key, String expectedValue){
this.stringRedisTemplate = stringRedisTemplate;
this.key = key;
this.expectedValue = expectedValue;
} /**
* 释放redis分布式锁
*/
@Override
public void unlock(){
List<String> keys = Collections.singletonList(key);
stringRedisTemplate.execute(new DefaultRedisScript<>(COMPARE_AND_DELETE, String.class), keys, expectedValue);
} @Override
public void close() throws Exception {
this.unlock();
}
}
}
扩展redisTemplate实现分布式锁的更多相关文章
- Redis实现分布式锁2
redisTemplate实现分布式锁 /** * 分布式锁-加锁 * @param key * @param value 当前时间+超时时间 System.currentTimeMillis()+t ...
- MasaFramework -- 锁与分布式锁
前言 什么是锁?什么是分布式锁?它们之间有什么样的关系? 什么是锁 加锁(lock)是2018年公布的计算机科学技术名词,是指将控制变量置位,控制共享资源不能被其他线程访问.通过加锁,可以确保在同一时 ...
- redis分布式锁Redisson扩展
如果大家项目中Redis是多机部署的可以来好好看看这篇实现,讲的非常好. 使用Redisson实现分布式锁,Spring AOP简化之 源码 Redisson概述 Redisson是一个在Redi ...
- Redisson分布式锁的简单使用
一:前言 我在实际环境中遇到了这样一种问题,分布式生成id的问题!因为业务逻辑的问题,我有个生成id的方法,是根据业务标识+id当做唯一的值! 而uuid是递增生成的,从1开始一直递增,那么在同一台机 ...
- spring boot redis分布式锁
随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁.分布式锁的实现有很多种,比如基于数据库. zookeeper 等,本文主要介绍使用 Redis 做分布式锁的方式,并封装成spring b ...
- spring boot redis分布式锁 (转)
一. Redis 分布式锁的实现以及存在的问题 锁是针对某个资源,保证其访问的互斥性,在实际使用当中,这个资源一般是一个字符串.使用 Redis 实现锁,主要是将资源放到 Redis 当中,利用其原子 ...
- 使用Redis分布式锁处理并发,解决超卖问题
一.使用Apache ab模拟并发压测 1.压测工具介绍 $ ab -n 100 -c 100 http://www.baidu.com/ -n表示发出100个请求,-c模拟100个并发,相当是100 ...
- Redis分布式锁—SETNX+Lua脚本实现篇
前言 平时的工作中,由于生产环境中的项目是需要部署在多台服务器中的,所以经常会面临解决分布式场景下数据一致性的问题,那么就需要引入分布式锁来解决这一问题. 针对分布式锁的实现,目前比较常用的就如下几种 ...
- 【Redis 分布式锁】(1)一把简单的“锁”
原文链接:https://www.changxuan.top/?p=1230 在单体架构向分布式集群架构演进的过程中,项目中必不可少的一个功能组件就是分布式锁.在开发团队有技术积累的情况下,做为团队的 ...
随机推荐
- SQL Server存储过程的删除方法
SQL Server存储过程的删除方法使我们经常会用到的,下面就为您介绍扩展存储过程的删除方法,如果您对SQL Server存储过程方面感兴趣的话,不妨一看. --清除SQL Server所有的危险扩 ...
- CVE-2014-1776 秘狐
传说中的IE秘狐 [CNNVD]Microsoft Internet Explorer 释放后重用漏洞(CNNVD-201404-530) Microsoft Internet Explorer(IE ...
- springboot 1.5.X junit测试
import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; impo ...
- Redis(二)CentOS7安装Redis4.0.10与集群搭建
一 Redis单机安装 1 Redis下载安装 1.1 检查依赖环境(Redis是C语言开发,编译依赖gcc环境) [root@node21 redis-]$ gcc -v -bash: gcc: c ...
- 【POJ】3270.Cow Sorting
题解 用到一点群论的知识! 我们发现把操作写成一个置换后,一定是单个置换圈的内进行操作,把置换圈进行扩大的操作不优 我们有两个办法,一个是用全局最小的换进来,代替这个圈里最小的值,交换操作完成后再换出 ...
- hdoj1863 畅通工程(Prime || Kruskal)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1863 思路 最小生成树问题,使用Prime算法或者Kruskal算法解决.这题在hdoj1233的基础 ...
- Django 学习总结(更新中)
1.常用命令 新建一个项目:django-admin.py startproject project-name 新建一个app:python manage.py startapp app-name 同 ...
- ref:LDAP入门
ref:https://www.jianshu.com/p/7e4d99f6baaf LDAP入门 首先要先理解什么是LDAP,当时我看了很多解释,也是云里雾里,弄不清楚.在这里给大家稍微捋一捋. 首 ...
- PHP中双引号引起的命令执行漏洞
前言 在PHP语言中,单引号和双引号都可以表示一个字符串,但是对于双引号来说,可能会对引号内的内容进行二次解释,这就可能会出现安全问题. 正文 举个简单例子 <?php $a = 1; $b = ...
- [python]缓存函数结果进redis
工作中遇到一个问题,就是有一些需要对数据库做全表扫描,而且对结果要求比较宽松的地方,总觉得可以找地方优化,比如暂时保存计算结果. 首先想起来的就是functools.lru_cache,但是可惜在py ...