分布式系统概念与设计(原书第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分布式锁自动延长过期时间相关推荐

  1. redis续期_redis分布式锁自动延长过期时间

    背景 项目组已经有个 分布式锁注解(参考前文<记一次分布式锁注解化>),但是在设置锁过期时间时,需要去预估业务耗时时间,如果锁的过期时间能根据业务运行时间自动调整,那使用的就更方便了. 思 ...

  2. redis实现轮询算法_【07期】Redis中是如何实现分布式锁的?

    点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面 ...

  3. redis 3.0 java 工具包_redis分布式锁工具包,提供纯Java方式调用

    redis-distributed-lock redis分布式锁工具包,提供纯Java方式调用,支持传统Spring工程, 为spring boot应用提供了starter,更方便快捷的调用. 项目结 ...

  4. **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java版)

    前言 分布式锁一般有三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁 本篇博客将介绍第二种方式,基于Redis实现分布式锁. 虽然网上已经有各种介绍Redi ...

  5. redis 分布式锁 看门狗_redis分布式锁原理及实现

    一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识. 所以咱们这篇文 ...

  6. redis set 超时_redis分布式锁3种实现方式对比分析总结

    我在这篇文章提到了分布式锁,但没有展开来讲,抛砖引玉,今天就来说说高并发服务编程中的redis分布式锁. 这里罗列出3种redis实现的分布式锁,并分别对比说明各自特点. Redis单实例分布式锁 实 ...

  7. **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java 版)

    点击上方"Java基基",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 原创 | Java 2020 超神之路,很肝~ 中文详细注释的开源项目 ...

  8. java 通过redis实现倒计时_突破Java面试(42) - Redis amp; ZooKeeper两种分布式锁实现的优劣...

    0 Github 1 面试题 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 2 考点分析 一般先问问你zk,然后 ...

  9. redis mysql 解决超卖_Redis 分布式锁解决超卖问题

    Redis 分布式锁解决超卖问题 1,Redis 事物介绍 1. Redis 事物是可以一次执行多个命令, 本质是一组命令的集合. 2. 一个事务中的所有命令都会序列化, 按顺序串行化的执行而不会被其 ...

最新文章

  1. AWK用法详解(转载)
  2. 啥都不如烂笔头,约翰霍普金斯大学新研究:学外语还得用手写
  3. STC89C52RC片内资源介绍
  4. asp.net2.0密码强度验证
  5. python scipy 稀疏矩阵详解
  6. 争分夺秒!一大批高校正在加紧扩建......
  7. 字符串处理 —— 回文串相关 —— Manacher 算法
  8. HTTP和HTTPS回顾
  9. AJAX 事件与事件对象
  10. qqxml图片代码_QQxml卡片代码合集超大图
  11. 洛谷 U5773 受望先锋
  12. 云计算时代迎接挑战方能脱颖而出
  13. 子网掩码的作用和用法
  14. 831数据结构与c语言试题,2018年广东工业大学计算机院831数据结构与C语言[专硕]之C程序设计考研核心题库...
  15. VMware虚拟机去虚拟化完整版教程|永久过强壳VMP、SE壳、GK盾、TMD教程|VMware去虚拟化吾爱汇编论坛教程完整版
  16. alt复制选区就会卡 ps_ps怎么复制选区相关常见问题解答
  17. hanlp分词学习笔记
  18. What is the difference between a theorem, a lemma, and a corollary?
  19. 关于破解的十个基本功
  20. xilinx官网下载vivado等软件工具无法下载原因,报错:xilinx官网 由于您的帐号输出合规验证失败,我们无法满足您的请求。

热门文章

  1. 孩子多大可以学python_少儿python教材适合多大的孩子?孩子接触起来困难吗?
  2. 【contacts】Phonebook电话本
  3. 新能源高压线束气密性测试时的手动密封方案和气动密封方案
  4. 【行研报告】短视频创作者作品发布时间研究报告—附下载链接
  5. block(程序块)
  6. 淘宝打标 卡首屏auction tag代码(关键词、黑搜、猜你喜欢、直通车、打标)
  7. l-aravel项目部署
  8. Oracle 12c备份与恢复
  9. 搜索引擎的网站入口登录
  10. 应用软件类的推荐用CS模式