扩展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 在单体架构向分布式集群架构演进的过程中,项目中必不可少的一个功能组件就是分布式锁.在开发团队有技术积累的情况下,做为团队的 ...
随机推荐
- 毕设demo写好
2015年1月20日 14:41:47 阶段性暂停!! 把运行结果截图给了老师,老师说先整理下文档,然后下学期来了再部署到服务器上. 那么,下学期来了,估计也要把Epm和CR1000什么的搞好了. 先 ...
- more命令 less命令
more命令是一个基于vi编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持vi中的关键字定位操作.more名单中内置了若干快捷键,常用的有H(获得帮助信息),Enter(向下翻滚一行), ...
- sicily 1176. Two Ends (Top-down 动态规划+记忆化搜索 v.s. Bottom-up 动态规划)
Description In the two-player game "Two Ends", an even number of cards is laid out in a ro ...
- Spark(十三)SparkSQL的自定义函数UDF与开窗函数
一 自定义函数UDF 在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_ ...
- Winsock—I/O模型之选择模型(一)
Winsock中提供了一些I/O模型帮助应用程序以异步方式在一个或多个套接字上管理I/O. 这样的I/O模型有六种:阻塞(blocking)模型,选择(select)模型,WSAAsyncSelect ...
- 模拟界面请求到web服务器
客户端 package com.lsw.client; import java.io.*; import java.net.*; import java.util.*; public class HT ...
- PHP 数组的添加和读取
在实际的开发中,会经常使用数组的添加和读取.这里把经常使用的操作记下来,以备以后查阅. <?php //一维数值数组 $list = array('wang','god'); $list[] = ...
- Java反射机制demo(七)—反射机制与工厂模式
Java反射机制demo(七)—反射机制与工厂模式 工厂模式 简介 工厂模式是最常用的实例化对象模式. 工厂模式的主要作用就是使用工厂方法代替new操作. 为什么要使用工厂模式?直接new不好吗? 直 ...
- CodeForces 140C New Year Snowmen(堆)
题面 CodeForces 题解 因为要保证两两不同,所以不能单纯的开堆来维护,堆维护一个二元组,个数为第一关键字,编号为第二关键字,对于一个相同的颜色,统计一下这个颜色的个数再用堆来维护就好了. # ...
- ARKit:增强现实技术在美团到餐业务的实践
前言 增强现实(Augmented Reality)是一种在视觉上呈现虚拟物体与现实场景结合的技术.Apple 公司在 2017 年 6 月正式推出了 ARKit,iOS 开发者可以在这个平台上使用简 ...