java redis set 过期时间_redis分布式锁自动延长过期时间
分布式系统概念与设计(原书第5版)
93.8元
包邮
(需用券)
去购买 >
背景项目组已经有个分布式锁注解(参考前文《记一次分布式锁注解化》),但是在设置锁过期时间时,需要去预估业务耗时时间,如果锁的过期时间能根据业务运行时间自动调整,那使用的就更方便了。
思路
思路参考了redisson:保留原先的可自定义设置过期时间,只有在没有设置过期时间(过期时间为默认值0)的情况下,才会启动自动延长。
申请锁时,设置一个延长过期时间,定时每隔延长过期时间的三分之一时间就重新设置过期时间(时期时间值为延长过期时间)。
为了防止某次业务由于异常而出现任务持续很久,从而长时间占有了锁,添加最大延期次数参数。
加锁用一个Map来存储需要续期的任务信息。
在加锁成功之后将任务信息放入Map,并启动延迟任务,延迟任务在执行延期动作前先检查下Map里锁数据是不是还是被当前任务持有。
每次续期任务完成并且成功之后,就再次启动延迟任务。
申请锁
复用之前的加锁方法,把延长过期时间作为加锁过期时间。public Lock acquireAndRenew(String lockKey, String lockValue, int lockWatchdogTimeout) {
return acquireAndRenew(lockKey, lockValue, lockWatchdogTimeout, 0);
}
public Lock acquireAndRenew(String lockKey, String lockValue, int lockWatchdogTimeout, int maxRenewTimes) {
if (lockKey == null || lockValue == null || lockWatchdogTimeout <= 0) {
return new Lock(this).setSuccess(false).setMessage("illegal argument!");
}
Lock lock = acquire(lockKey, lockValue, lockWatchdogTimeout);
if (!lock.isSuccess()) {
return lock;
}
expirationRenewalMap.put(lockKey, new RenewLockInfo(lock));
scheduleExpirationRenewal(lockKey, lockValue, lockWatchdogTimeout, maxRenewTimes, new AtomicInteger());
return lock;
}
定时续期
当前锁还未被释放(Map里还有数据),并且当前延期任务执行成功,则继续下一次任务。private void scheduleExpirationRenewal(String lockKey, String lockValue, int lockWatchdogTimeout,
int maxRenewTimes, AtomicInteger renewTimes) {
ScheduledFuture> scheduledFuture = scheduledExecutorService.schedule(() -> {
try {
if (!renewExpiration(lockKey, lockValue, lockWatchdogTimeout)) {
log.debug("dislock renew[{}:{}] fail!", lockKey, lockValue);
return;
}
if (maxRenewTimes > 0 && renewTimes.incrementAndGet() == maxRenewTimes) {
log.info("dislock renew[{}:{}] override times[{}]!", lockKey, lockValue, maxRenewTimes);
return;
}
scheduleExpirationRenewal(lockKey, lockValue, lockWatchdogTimeout, maxRenewTimes, renewTimes);
} catch (Exception e) {
log.error("dislock renew[{}:{}] error!", lockKey, lockValue, e);
}
}, lockWatchdogTimeout / 3, TimeUnit.MILLISECONDS);
RenewLockInfo lockInfo = expirationRenewalMap.get(lockKey);
if (lockInfo == null) {
return;
}
lockInfo.setRenewScheduledFuture(scheduledFuture);
}
private boolean renewExpiration(String lockKey, String lockValue, int lockWatchdogTimeout) {
RenewLockInfo lockInfo = expirationRenewalMap.get(lockKey);
if (lockInfo == null) {
return false;
}
if (!lockInfo.getLock().getLockValue().equals(lockValue)) {
return false;
}
List keys = Lists.newArrayList(lockKey);
List args = Lists.newArrayList(lockValue, String.valueOf(lockWatchdogTimeout));
return (long) jedisTemplate.evalsha(renewScriptSha, keys, args) > 0;
}
延期脚本public void init() {
……
String renewScript = "if redis.call('get',KEYS[1]) == ARGV[1] then \n" +
" redis.call('pexpire', KEYS[1], ARGV[2]) \n" +
" return 1 \n " +
" end \n" +
" return 0";
renewScriptSha = jedisTemplate.scriptLoad(renewScript);
}
释放
执行释放之前,先将数据从Map里清除掉。public boolean release(Lock lock) {
if (!ifReleaseLock(lock)) {
return false;
}
// 放在redis脚本前面,防止redis删除失败,而map没有清理,从而导致redis无限期续期
try {
RenewLockInfo lockInfo = expirationRenewalMap.get(lock.getLockKey());
if (lockInfo != null) {
ScheduledFuture> scheduledFuture = lockInfo.getRenewScheduledFuture();
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
}
} catch (Exception e) {
log.error("dislock cancel renew scheduled[{}:{}] error!", lock.getLockKey(), lock.getLockValue(), e);
}
expirationRenewalMap.remove(lock.getLockKey());
List keys = Lists.newArrayList(lock.getLockKey());
List args = Lists.newArrayList(lock.getLockValue());
return (long) jedisTemplate.evalsha(releaseScriptSha, keys, args) > 0;
}
注解改造
注解类
注解增加两个参数,并且原先的过期时间参数默认值改为0,即默认启动自动延期。@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface DisLock {
int DEFAULT_EXPIRE = -1;
int DEFAULT_LOCK_WATCHDOG_TIMEOUT = 30000;
…… // 其他参数
/**
* 默认key过期时间,单位毫秒
*
* @return long
* @author
* @date 2020-03-17 22:50
*/
int expire() default DEFAULT_EXPIRE;
/**
* 监控锁的看门狗超时时间,单位毫秒,参数用于自动续约过期时间
* 参数只适用于分布式锁的加锁请求中未明确使用expire参数的情况(expire等于默认值DEFAULT_EXPIRE)。
*
* @return int
* @author
* @date 2020-10-14 11:08
*/
int lockWatchdogTimeout() default DEFAULT_LOCK_WATCHDOG_TIMEOUT;
/**
* 最大续期次数,用于防止业务进程缓慢在导致长时间占有锁
*
* @return int 大于0时有效,小于等于0表示无限制
* @author
* @date 2020-10-15 16:23
*/
int maxRenewTimes() default 0;
}
注解处理类JedisDistributedLock.Lock lock = jedisDistributedLock.acquire(key, value, disLock.expire());
改成JedisDistributedLock.Lock lock;
if (ifRenew(disLock)) {
lock = jedisDistributedLock
.acquireAndRenew(key, value, disLock.lockWatchdogTimeout(), disLock.maxRenewTimes());
} else {
lock = jedisDistributedLock.acquire(key, value, disLock.expire());
}
protected boolean ifRenew(DisLock disLock) {
return disLock.expire() == DisLock.DEFAULT_EXPIRE;
}
java 11官方入门(第8版)教材
79.84元
包邮
(需用券)
去购买 >
java redis set 过期时间_redis分布式锁自动延长过期时间相关推荐
- redis续期_redis分布式锁自动延长过期时间
背景 项目组已经有个 分布式锁注解(参考前文<记一次分布式锁注解化>),但是在设置锁过期时间时,需要去预估业务耗时时间,如果锁的过期时间能根据业务运行时间自动调整,那使用的就更方便了. 思 ...
- redis实现轮询算法_【07期】Redis中是如何实现分布式锁的?
点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面 ...
- redis 3.0 java 工具包_redis分布式锁工具包,提供纯Java方式调用
redis-distributed-lock redis分布式锁工具包,提供纯Java方式调用,支持传统Spring工程, 为spring boot应用提供了starter,更方便快捷的调用. 项目结 ...
- **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java版)
前言 分布式锁一般有三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁 本篇博客将介绍第二种方式,基于Redis实现分布式锁. 虽然网上已经有各种介绍Redi ...
- redis 分布式锁 看门狗_redis分布式锁原理及实现
一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识. 所以咱们这篇文 ...
- redis set 超时_redis分布式锁3种实现方式对比分析总结
我在这篇文章提到了分布式锁,但没有展开来讲,抛砖引玉,今天就来说说高并发服务编程中的redis分布式锁. 这里罗列出3种redis实现的分布式锁,并分别对比说明各自特点. Redis单实例分布式锁 实 ...
- **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java 版)
点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 原创 | Java 2020 超神之路,很肝~ 中文详细注释的开源项目 ...
- java 通过redis实现倒计时_突破Java面试(42) - Redis amp; ZooKeeper两种分布式锁实现的优劣...
0 Github 1 面试题 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 2 考点分析 一般先问问你zk,然后 ...
- redis mysql 解决超卖_Redis 分布式锁解决超卖问题
Redis 分布式锁解决超卖问题 1,Redis 事物介绍 1. Redis 事物是可以一次执行多个命令, 本质是一组命令的集合. 2. 一个事务中的所有命令都会序列化, 按顺序串行化的执行而不会被其 ...
最新文章
- AWK用法详解(转载)
- 啥都不如烂笔头,约翰霍普金斯大学新研究:学外语还得用手写
- STC89C52RC片内资源介绍
- asp.net2.0密码强度验证
- python scipy 稀疏矩阵详解
- 争分夺秒!一大批高校正在加紧扩建......
- 字符串处理 —— 回文串相关 —— Manacher 算法
- HTTP和HTTPS回顾
- AJAX 事件与事件对象
- qqxml图片代码_QQxml卡片代码合集超大图
- 洛谷 U5773 受望先锋
- 云计算时代迎接挑战方能脱颖而出
- 子网掩码的作用和用法
- 831数据结构与c语言试题,2018年广东工业大学计算机院831数据结构与C语言[专硕]之C程序设计考研核心题库...
- VMware虚拟机去虚拟化完整版教程|永久过强壳VMP、SE壳、GK盾、TMD教程|VMware去虚拟化吾爱汇编论坛教程完整版
- alt复制选区就会卡 ps_ps怎么复制选区相关常见问题解答
- hanlp分词学习笔记
- What is the difference between a theorem, a lemma, and a corollary?
- 关于破解的十个基本功
- xilinx官网下载vivado等软件工具无法下载原因,报错:xilinx官网 由于您的帐号输出合规验证失败,我们无法满足您的请求。