Redis

RedisTemplate使用lua脚本分布式锁

2019-06-19  本文已影响0人  f22448cd5541

Jedis进行分布式锁

Object obj = jedis.eval("if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('expire', KEYS[1], ARGV[2]) return 'true' else return 'false' end", 1, lock, val, "10");

我们使用 jedis 原生的对象执行lua脚本的话,非常简单,也很直观。但是真实的项目里,我们经常会使用Spring Cloud框架,该框架已经集成了 RedisTemplate 这个类,开放了很多对redis的api。笔者在使用RedisTemplate执行lua脚本,遇到一些困难。摸索了一番,最后解决了,趁此机会记录下来。

RedisTemplate执行lua

/**
 * <p>
 * 分布式锁定义
 * </p>
 *
 */
public interface DistributedLock {

    /**
     * <p>
     *     上锁,默认的锁的时间是 10s
     * </p>
     */
    boolean lock(String lock, String val);

    /**
     * <p>
     *     上锁
     * </p>
     */
    boolean lock(String lock, String val, int second);

    /**
     * <p>
     *     释放锁
     * </p>
     */
    void unlock(String lock, String val);
}

这里,序列化的方式,采用的都是String方式。实现类如下:

public class RedisLock implements DistributedLock {

    public static final int DEFAULT_SECOND_LEN = 10; // 10 s

    private RedisTemplate<String, String> redisTemplate;

    private static final String LOCK_LUA = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('expire', KEYS[1], ARGV[2]) return 'true' else return 'false' end";
    private static final String UNLOCK_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) end return 'true' ";

    private RedisScript lockRedisScript;
    private RedisScript unLockRedisScript;

    private RedisSerializer<String> argsSerializer;
    private RedisSerializer<String> resultSerializer;

    /**
     * 初始化lua 脚本
     */
    public void init(RedisTemplate<String, String> redisTemplate) {

        this.redisTemplate = redisTemplate;

        argsSerializer = new StringRedisSerializer();
        resultSerializer = new StringRedisSerializer();

        lockRedisScript = RedisScript.of(LOCK_LUA, String.class);
        unLockRedisScript = RedisScript.of(UNLOCK_LUA, String.class);
    }

    @Override
    public boolean lock(String lock, String val) {
        return this.lock(lock, val, DEFAULT_SECOND_LEN);
    }

    @Override
    public boolean lock(String lock, String val, int second) {
        List<String> keys = Collections.singletonList(lock);
        String flag = redisTemplate.execute(lockRedisScript, argsSerializer, resultSerializer, keys, val, String.valueOf(second));
        return Boolean.valueOf(flag);
    }

    @Override
    public void unlock(String lock, String val) {
        List<String> keys = Collections.singletonList(lock);
        redisTemplate.execute(unLockRedisScript, argsSerializer, resultSerializer, keys, val);
    }


}

使用如下,需要先将该类初始化到Spring容器里,如下:

@Bean
public RedisLock redisLock(RedisTemplate<String, String> redisTemplate) {
   RedisLock redisLock = new RedisLock();
   redisLock.init(redisTemplate);
   return redisLock;
}

使用该锁对象,只需要将 RedisLock 注入到所需要使用的业务类内即可。

上一篇下一篇

猜你喜欢

热点阅读