Redis(十二):redisson分布式锁中的看门狗机制 您所在的位置:网站首页 分布式书籍 Redis(十二):redisson分布式锁中的看门狗机制

Redis(十二):redisson分布式锁中的看门狗机制

2023-06-08 13:10| 来源: 网络整理| 查看: 265

Redisson 锁的加锁机制

自定义redis分布式锁无法自动续期,比如,一个锁设置了1分钟超时释放,如果拿到这个锁的线程在一分钟内没有执行完毕,那么这个锁就会被其他线程拿到,可能会导致严重的线上问题,在秒杀场景下,很容易因为这个缺陷导致的超卖了。

Redisson 锁加锁流程:线程去获取锁,获取成功则执行lua脚本,保存数据到redis数据库。如果获取失败: 一直通过while循环尝试获取锁(可自定义等待时间,超时后返回失败)。Redisson提供的分布式锁是支持锁自动续期的,也就是说,如果线程仍旧没有执行完,那么redisson会自动给redis中的目标key延长超时时间,这在Redisson中称之为 Watch Dog 机制。

image.png Redisson 加解锁API public void test() throws Exception{ RLock lock = redissonClient.getLock("guodong"); // 拿锁失败时会不停的重试 // 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s lock.lock(); // 具有Watch Dog 自动延期机制 默认续30s // 尝试拿锁10s后停止重试,返回false 具有Watch Dog 自动延期机制 默认续30s boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 没有Watch Dog // 尝试获取锁10秒,如果获取不到则放弃 lock.lock(10, TimeUnit.SECONDS); // 没有Watch Dog // 尝试获取锁,等待100秒,持有锁10秒钟 boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS); Thread.sleep(40000L); lock.unlock(); }

lock() 方法是阻塞获取锁的方式,如果当前锁被其他线程持有,则当前线程会一直阻塞等待获取锁,直到获取到锁或者发生超时或中断等情况才会结束等待。该方法获取到锁之后可以保证线程对共享资源的访问是互斥的,适用于需要确保共享资源只能被一个线程访问的场景。Redisson 的 lock() 方法支持可重入锁和公平锁等特性,可以更好地满足多线程并发访问的需求。

而 tryLock() 方法是一种非阻塞获取锁的方式,在尝试获取锁时不会阻塞当前线程,而是立即返回获取锁的结果,如果获取成功则返回 true,否则返回 false。Redisson 的 tryLock() 方法支持加锁时间限制、等待时间限制以及可重入等特性,可以更好地控制获取锁的过程和等待时间,避免程序出现长时间无法响应等问题。

默认情况下,看门狗的续期时间是30s,也可以通过修改Config.lockWatchdogTimeout来另行指定。另外Redisson 还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。

watch dog缺点

redisson看门狗虽然能保证在线程没有执行完毕时,锁不会释放,对于秒杀这种强一致性的场景是适用的,但是对于防重这种场景,是不适用的,在高并发情况下,会导致接口性能下降。

高并发防重时,如果加锁失败就快速失败,这时候可以使用自定义锁,或者tryLock,如下

方式一:自定义redis分布式锁

public class RedisTool { private static final String LOCK_SUCCESS = "OK"; //NX|XX, NX -- Only set the key if it does not already exist; // XX -- Only set the key if it already exist. private static final String SET_IF_NOT_EXIST = "NX"; //EX|PX, expire time units: EX = seconds; PX = milliseconds private static final String SET_WITH_EXPIRE_TIME = "PX"; private static volatile JedisPool jedisPool = null; public static JedisPool getRedisPoolUtil() { if(null == jedisPool ){ synchronized (RedisTool.class){ if(null == jedisPool){ GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setMaxTotal(100); poolConfig.setMaxIdle(10); poolConfig.setMaxWaitMillis(100*1000); poolConfig.setTestOnBorrow(true); jedisPool = new JedisPool(poolConfig,"192.168.10.151",6379); } } } return jedisPool; } /** * 尝试获取分布式锁 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */ public static boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) { Jedis jedis = jedisPool.getResource(); try { String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; }catch (Exception e){ return false; }finally { jedisPool.returnResource(jedis); } } private static final Long RELEASE_SUCCESS = 1L; /** * 释放分布式锁 * @param lockKey 锁 * @param requestId 请求标识 * @return 是否释放成功 */ public static boolean releaseDistributedLock(String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; //如果使用的是切片shardedJedis,那么需要先获取到jedis, //Jedis jedis = shardedJedis.getShard(key); Jedis jedis = jedisPool.getResource(); try { Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; }catch (Exception e){ return false; }finally { jedisPool.returnResource(jedis); } } }

方式二:redisson tryLock

RLock lock = redissonClient.getLock("Export:create:" + Context.get().getCorpId()); try { //尝试加锁,最多等待0秒,上锁以后5秒自动解锁 if (lock.tryLock(0, 5, TimeUnit.SECONDS)) { //业务处理 } else { Assert.isTrue(false, "排队中,请稍后重试!"); } } catch (InterruptedException e) { Assert.isTrue(false, "请勿重复操作!"); } finally { if (lock.isLocked()) { lock.unlock(); } }

总体上来说,防重场景下,优先使用方式一,逻辑简单执行性能高。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有