分享一个redis分布式锁的demo

首先说一下需要使用分布式锁的场景

  • 多实例部署,分布式系统中经常会遇到,基于jvm的锁无法满足多实例中锁的需求, synchronized,Lock只能控制在当前JVM中的资源竞争。基于数据库实现的话肯能会造成IO压力,甚至死锁,其本身的效率也比较低。zk的实现方式也是可以的。

 

redis作为分布式锁要注意的事项,还有用到的一些功能

注意事项:

  • 互斥性,同一时刻,智能有一个客户端持有锁。
  • 防止死锁发生,如果持有锁的客户端崩溃没有主动释放锁,也要保证锁可以正常释放及其他客户端可以正常加锁。
  • 防止误删,加锁和释放锁必须是同一个客户端。
  • 高可用,如果只有Redis服务端负责加锁,这个Redis挂了怎么办?

用到的功能:

  • setnx(key, value): set if not exits,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。
  • expire(key, seconds):设置key-value的有效期为seconds秒。
  • watch(key):用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
  • unwatch:取消 WATCH 命令对所有 key 的监视
  • multi:标记一个事务块的开始。多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。
  • del(key):在 key 存在时删除 key。

代码:

注释TODO的地方需要自行修改处理,日志也自行替换吧。思路解释写在注释中了,方便理解。

redis客户端:

/**
 * reids 客户端
 */
public class RedisManager {

    private static JedisPool jedisPool;
    static{
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(20);
        jedisPoolConfig.setMaxIdle(10);
        jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379);
    }

    public static Jedis getJedis() throws Exception {
        if(null != jedisPool){
            return jedisPool.getResource();
        }
        throw  new Exception("Jedispool was not init");
    }
}

获取锁:

/**
     * 获取分布式锁
      * @param key
     * @param timeout 超时时限
     * @return
     */
    public String getLock(String key,int timeout){
         //TODO 过期时间从缓存中取 配置化
        try {
            //获得客户端实例
            Jedis jedis = RedisManager.getJedis();
            //生成随机锁号,解锁时用于判断,是否为当前线程持有锁
            String value = UUID.randomUUID().toString();
            //不能无限等待,超出时间放弃自旋
            long end = System.currentTimeMillis() + timeout;
            //判断是否超出时限  并且阻塞
           while(System.currentTimeMillis()

释放锁:

 /**
     * 释放锁(判断是否为当前线程持有)
     * @param key
     * @param value
     * @return
     */
    public boolean releaseLock(String key,String value){
        try {
            Jedis jedis = RedisManager.getJedis();
            while (true) {
            //监控一个或多个KEY 一旦KEY 被删除 ,后面事务的代码不会被执行
            jedis.watch(key);
            //判断是否为当前线程持有锁
            if(value.equals(jedis.get(key))) {
                //开启事务
                Transaction transaction = jedis.multi();
                transaction.del(key);
                List list = transaction.exec();
                //没有指令集执行成功 解锁失败
                //TODO 关注事务执行过程(监视解锁前 人为变动锁可能会出现的问题)
                if (list == null) {
                    continue;
                }
                System.out.println("锁释放成功,key【"+key+"】,value【"+value+"】");
                return true;
            }
            jedis.unwatch();
                break;
            }
        } catch (Exception e) {
            //TODO 添加日志
            System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】解锁失败"+e.getCause());
            e.printStackTrace();
        }
        return false;
    } 
  

测试:

    public static void main(String... args) throws Exception {
        RedisLock redisLock = new RedisLock();
//        Jedis jedis = RedisManager.getJedis();
//        String curLock = jedis.get("lock:testLock");
//        System.out.println(curLock);
//        //强制解锁 清空
//        jedis.del("lock:testLock");
        String key = "lock:testLock";
        String lockId = redisLock.getLock(key,10000);
        if(null != lockId){
            System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】获得锁成功,"+"锁KEY【"+key+"】"+"锁序号:【"+lockId+"】");
        }else{
            System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】获得锁失败");
        }
        redisLock.releaseLock("lock:testLock",lockId);
        System.out.println("完成");
}

入门级demo 主要用于理解思路,demo未经过生产考验!!!!

你可能感兴趣的:(工具配置)