基于setnx加锁,基于lua实现锁释放。

举报
琴岛蛏子 发表于 2022/03/21 00:06:53 2022/03/21
【摘要】 基于setnx加锁,基于lua实现锁释放。实现了根据 key 及唯一标识 requestId进行加锁,并设置了超时时间;释放锁时根据key及requestId进行释放。SET_IF_ABSENT 没有key才设置,expire 设置超时时间,作为原子性操作RedisCallback<Boolean> callback = (connection) -> { return connec...

基于setnx加锁,基于lua实现锁释放。

实现了根据 key 及唯一标识 requestId进行加锁,并设置了超时时间;释放锁时根据key及requestId进行释放。

SET_IF_ABSENT 没有key才设置,expire 设置超时时间,作为原子性操作

RedisCallback<Boolean> callback = (connection) -> {
    return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);
};
return (Boolean)redisTemplate.execute(callback)

直接上代码,可以直接用的工具类

@Slf4j
public class RedisDistributedLock {
 
  private RedisTemplate redisTemplate;
 
  public RedisDistributedLock(RedisTemplate redisTemplate){
    this.redisTemplate = redisTemplate;
  }
 
  public static final String UNLOCK_LUA;
 
  /**
   * 释放锁脚本,原子操作
   */
  static {
    StringBuilder sb = new StringBuilder();
    sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
    sb.append("then ");
    sb.append("    return redis.call(\"del\",KEYS[1]) ");
    sb.append("else ");
    sb.append("    return 0 ");
    sb.append("end ");
    UNLOCK_LUA = sb.toString();
  }
 
 
  /**
   * 获取分布式锁,原子操作
   * @param lockKey
   * @param requestId 唯一ID, 可以使用UUID.randomUUID().toString();
   * @param expire
   * @param timeUnit
   * @return
   */
  public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {
    try{
      RedisCallback<Boolean> callback = (connection) -> {
        return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);
      };
      return (Boolean)redisTemplate.execute(callback);
    } catch (Exception e) {
      log.error("redis lock error.", e);
    }
    return false;
  }
 
  /**
   * 释放锁
   * @param lockKey
   * @param requestId 唯一ID
   * @return
   */
  public boolean releaseLock(String lockKey, String requestId) {
    RedisCallback<Boolean> callback = (connection) -> {
      return connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN ,1, lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")));
    };
    return (Boolean)redisTemplate.execute(callback);
  }
 
  /**
   * 获取Redis锁的value值
   * @param lockKey
   * @return
   */
  public String get(String lockKey) {
    try {
      RedisCallback<String> callback = (connection) -> {
        return new String(connection.get(lockKey.getBytes()), Charset.forName("UTF-8"));
      };
      return (String)redisTemplate.execute(callback);
    } catch (Exception e) {
      log.error("get redis occurred an exception", e);
    }
    return null;
  }
 
}

此工具简易轻量,基本满足加锁的一些场景。

VS redission分布式锁

redisson实现了加锁尝试时间

看门狗延长锁持有时间。

redission支持多样锁如可重入锁、读写锁、信号量等。

直接用setNx的问题,无超时时间程序意外挂掉,导致key不释放,好像新版本有setnx+过期时间的方法。

释放锁直接用delete 可能删除非当前线程的锁,增加唯一value校验,两步操作非原子性。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。