redisLockRegistry分布式锁

一:总结下分布式锁使用原理:
1.获取内存锁对象:
Lock lock = redisLockRegistry.obtain(key);
内部解析:

【注】该地方返回的是,如果已有的返回之前的对象,否则new个新的对象。

private final Map<String, RedisLock> locks = new ConcurrentHashMap<>();
@Override
public Lock obtain(Object lockKey) {
	Assert.isInstanceOf(String.class, lockKey);
	String path = (String) lockKey;
	return this.locks.computeIfAbsent(path, RedisLock::new);
}
  default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }

    default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }
  1. 获取锁过程:
    lock.tryLock(String lockKey, long seconds)

内部解析:
①首先使用缓存ReentrantLock锁,这样同一机器不用去请求redis
②其次使用redis锁,lua脚本
这里说明:同一台机器重复获取redis锁,相当于重入,超时时间加一倍。

private static final String OBTAIN_LOCK_SCRIPT =
		"local lockClientId = redis.call('GET', KEYS[1])\n" +
				"if lockClientId == ARGV[1] then\n" +
				"  redis.call('PEXPIRE', KEYS[1], ARGV[2])\n" +
				"  return true\n" +
				"elseif not lockClientId then\n" +
				"  redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])\n" +
				"  return true\n" +
				"end\n" +
				"return false";
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
   long now = System.currentTimeMillis();
   if (!this.localLock.tryLock(time, unit)) {
      return false;
   }
   try {
      long expire = now + TimeUnit.MILLISECONDS.convert(time, unit);
      boolean acquired;
      while (!(acquired = obtainLock()) && System.currentTimeMillis() < expire) { /SONAR
         Thread.sleep(100); /SONAR
      }
      if (!acquired) {
         this.localLock.unlock();
      }
      return acquired;
   }
   catch (Exception e) {
      this.localLock.unlock();
      rethrowAsLockException(e);
   }
   return false;
}

3.获取到锁之后,执行正常流程完成后,涉及到释放锁。

if(Objects.nonNull(lock) && hasLock){
lock.unlock();
redisLockRegistry.expireUnusedOlderThan(-300);
}

① 首先释放redis锁。
②再根据超时时间释放缓存锁数据。

@Override
public void expireUnusedOlderThan(long age) {
   Iterator<Map.Entry<String, RedisLock>> iterator = this.locks.entrySet().iterator();
   long now = System.currentTimeMillis();
   while (iterator.hasNext()) {
      Map.Entry<String, RedisLock> entry = iterator.next();
      RedisLock lock = entry.getValue();
      if (now - lock.getLockedAt() > age && !lock.isAcquiredInThisProcess()) {
         iterator.remove();
      }
   }
}

不过有个问题:
在上面释放redis锁之后,在释放缓存锁数据时,是根据是否是本机数据,并且超时时间来删除的。此时有个场景会有问题:
A,线程执行完释放锁
B线程进来等待锁
C线程开始来执行。
此时内存锁只要A释放了,任何一个线程执行完都可以释放这把内存锁,导致C是锁不住的问题。
就会看到并发了。所以上面-300设置是有问题的

public boolean isAcquiredInThisProcess() {
			return RedisLockRegistry.this.clientId.equals(
					RedisLockRegistry.this.redisTemplate.boundValueOps(this.lockKey).get());
		}

【解决方法】
超时时间跟redis锁默认超时时间保持一致;
redisLockRegistry.expireUnusedOlderThan(60000L);

!lock.isAcquiredInThisProcess()表示是其他机器锁住了,或者是其他机器已经释放两种情况,所以删除没有问题。
-----未测试哈

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
RedisLockRegistry 是 Spring Integration 提供的一种基于 Redis分布式锁实现。它使用 Redis 实现了锁的管理和协调,提供了一种简单而有效的方式来确保在分布式环境中的资源互斥访问。 使用 RedisLockRegistry 首先需要配置 Redis 连接信息,然后通过创建 RedisLockRegistry 对象来获取分布式锁实例。下面是一个简单的示例: ```java @Configuration @EnableIntegration public class RedisLockConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { // 配置 Redis 连接工厂 RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("localhost",6379); return new LettuceConnectionFactory(config); } @Bean public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { // 创建 RedisLockRegistry 实例,并指定 Redis 连接工厂 return new RedisLockRegistry(redisConnectionFactory, "my-locks"); } } ``` 在需要使用分布式锁的地方,可以通过 RedisLockRegistry 来获取锁并执行相应的操作。下面是一个简单的示例: ```java @Autowired private RedisLockRegistry redisLockRegistry; public void performTaskWithLock() { Lock lock = redisLockRegistry.obtain("my-lock"); // 获取名为 "my-lock" 的分布式锁 try { if (lock.tryLock()) { // 尝试获取锁 // 执行需要互斥访问的操作 } else { // 锁被其他线程占用,执行相应的逻辑 } } finally { lock.unlock(); // 释放锁 } } ``` RedisLockRegistry 会在 Redis 中创建相应的键来表示锁的状态,并使用 Redis 的原子操作来确保锁的正确获取和释放。这样可以保证在分布式场景下的资源互斥访问。 需要注意的是,在使用 RedisLockRegistry 进行分布式锁管理时,需要确保 Redis 的可用性和性能,以及合理设置锁的超时时间和重试机制,以防止死锁和长时间的资源占用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值