redis实现分布式锁

来源:互联网 发布:1977特纳里夫空难 知乎 编辑:程序博客网 时间:2024/06/05 17:16

1.基本思想

 http://doc.redisfans.com/string/set.html 有说明 如下

命令 SET resource-name anystring NX EX max-lock-time 是一种在 Redis 中实现锁的简单方法。

客户端执行以上的命令:

  • 如果服务器返回 OK ,那么这个客户端获得锁。
  • 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。

设置的过期时间到达之后,锁将自动释放。(必须设置过期时间,以防获取锁的机器崩溃没有释放锁,其他机器永远不能获得锁)

可以通过以下修改,让这个锁实现更健壮:

  • 不使用固定的字符串作为键的值,而是设置一个不可猜测(non-guessable)的长随机字符串,作为口令串(token)。
  • 不使用 DEL 命令来释放锁,而是发送一个 Lua 脚本,这个脚本只在客户端传入的值和键的口令串相匹配时,才对键进行删除。

这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。

以下是一个简单的解锁脚本示例:

if redis.call("get",KEYS[1]) == ARGV[1]then    return redis.call("del",KEYS[1])else    return 0end

2.代码实现

import redis.clients.jedis.HostAndPort;import redis.clients.jedis.JedisCluster;import redis.clients.jedis.exceptions.JedisNoScriptException;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;/** * 分布式锁 */public class RedisLock {    private JedisCluster jedisCluster;    private String value;    private static final String OK = "OK";    private final int defaultLockExpireTime;    private static String releaseLockLua =            "if redis.call('get', KEYS[1]) == ARGV[1]" +                    " then" +                    "     return redis.call('del', KEYS[1])" +                    " else" +                    "     return 0" +                    " end";    private String key;    private static String sha;    public RedisLock(JedisCluster jedisCluster, String key) {        this.jedisCluster = jedisCluster;        this.key = key;        this.defaultLockExpireTime = 2000;    }    public RedisLock(JedisCluster jedisCluster, String key, int expireTime) {        this.jedisCluster = jedisCluster;        this.key = key;        this.defaultLockExpireTime = expireTime;    }    /**     * 无限阻塞直到获取到锁     */    public void lock() {        value = UUID.randomUUID().toString();        for (; ; ) {            //不存在则添加 且设置过期时间(单位ms            String status = jedisCluster.set(key, value, "NX", "PX", defaultLockExpireTime);            //获得锁            if (OK.equals(status)) {                break;            }            try {                Thread.sleep(2);            } catch (Exception e) {                //ignore            }        }    }    /**     * 尝试获取锁 超时返回     *     * @param awitTime 等待时间     * @return 是否成功获得锁     */    public boolean tryLock(long awitTime) {        value = UUID.randomUUID().toString();        long expireTime = System.currentTimeMillis() + awitTime;        while (System.currentTimeMillis() < expireTime) {            //不存在则添加 且设置过期时间(单位ms            String status = jedisCluster.set(key, value, "NX", "PX", defaultLockExpireTime);            //获得锁            if (OK.equals(status)) {                return true;            }            try {                Thread.sleep(2);            } catch (Exception e) {                //ignore            }        }        return false;    }    /**     * 尝试获取锁 立即返回     *     * @return 是否成功获得锁     */    public boolean tryLock() {        value = UUID.randomUUID().toString();        //不存在则添加 且设置过期时间(单位ms        String status = jedisCluster.set(key, value, "NX", "PX", defaultLockExpireTime);        //获得锁        return OK.equals(status);    }    /**     * 释放锁 释放失败则抛异常     */    public void unlockWithExcepion() {        if (sha == null) {            sha = jedisCluster.scriptLoad(RedisLock.releaseLockLua, "releaseLockLua");        }        Long eval;        try {            eval = (Long) jedisCluster.evalsha(sha, Collections.singletonList(key), Collections.singletonList(value));        } catch (JedisNoScriptException e) {            eval = (Long) jedisCluster.eval(releaseLockLua, Collections.singletonList(key), Collections.singletonList(value));        }        if (eval == 0) {            throw new RuntimeException("释放锁失败,已超时");        }    }    /**     * 释放锁     *     * @return 锁是否释放成功     */    public boolean unlock() {        if (sha == null) {            sha = jedisCluster.scriptLoad(RedisLock.releaseLockLua, "releaseLockLua");        }        Long eval;        try {            eval = (Long) jedisCluster.evalsha(sha, Collections.singletonList(key), Collections.singletonList(value));        } catch (JedisNoScriptException e) {            eval = (Long) jedisCluster.eval(releaseLockLua, Collections.singletonList(key), Collections.singletonList(value));        }        return eval == 1;    }    public static void main(String[] args) throws InterruptedException {        HostAndPort hostAndPort1 = new HostAndPort("ip1", 20621);        HostAndPort hostAndPort2 = new HostAndPort("ip2", 20622);        HostAndPort hostAndPort3 = new HostAndPort("ip3", 20622);        Set<HostAndPort> set = new HashSet<>();        set.add(hostAndPort1);        set.add(hostAndPort2);        set.add(hostAndPort3);        JedisCluster jedisCluster = new JedisCluster(set);        RedisLock redisLock = new RedisLock(jedisCluster, "redis:zhp");        redisLock.lock();        System.out.println("处理数据");        redisLock.unlock();    }}


原创粉丝点击