zoukankan      html  css  js  c++  java
  • Redis分布式锁实现

    Redis实现分布锁

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

    客户端执行以上的命令:

    如果服务器返回 OK ,那么这个客户端获得锁。 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。 设置的过期时间到达之后,锁将自动释放。

    从2.6.12版本开始,redis为SET命令增加了一系列选项:

    • EX seconds – Set the specified expire time, in seconds.
    • PX milliseconds – Set the specified expire time, in milliseconds.
    • NX – Only set the key if it does not already exist.
    • XX – Only set the key if it already exist.

    • EX seconds – 设置键key的过期时间,单位时秒
    • PX milliseconds – 设置键key的过期时间,单位时毫秒
    • NX – 只有键key不存在的时候才会设置key的值
    • XX – 只有键key存在的时候才会设置key的值

    注意: 由于SET命令加上选项已经可以完全取代SETNXSETEXPSETEX的功能,所以在将来的版本中,redis可能会不推荐使用并且最终抛弃这几个命令。

    代码:

     1 package cn.wywk.yac.comm.redis;
     2 
     3 
     4 import redis.clients.jedis.Jedis;
     5 
     6 /**
     7  * ClassName: redis分布式锁实现 <br/>
     8  * date: 2017年2月17日 上午10:23:24 <br/>
     9  * 
    10  * @author 13414wuxinyu
    11  */
    12 public class RedisLock {
    13 
    14     private  final static String lua = //
    15             "if redis.call("get",KEYS[1]) == ARGV[1]
    " //
    16                     + "then
    " //
    17                     + "  return redis.call("del",KEYS[1])
    " //
    18                     + "else
    " //
    19                     + "  return 0
    " //
    20                     + "end
    ";
    21     
    22     /**
    23      * acquire:分布式加锁
    24      * @param key 锁的key
    25      * @param value value
    26      * @param expires 持有锁的时间-秒
    27      * @param timeout 获取锁的最大等待时间_毫秒
    28      * @return
    29      * @author 13414
    30      * 2017年3月2日
    31      */
    32     public boolean acquire(Jedis jedis,String key,String value,long expires,int timeout) throws InterruptedException {
    33         while (timeout >= 0) {
    34             if("OK".equals(jedis.set(key,value, "NX","EX",expires))){
    35                 return true;
    36             }
    37             timeout -= 100;
    38             Thread.sleep(100);
    39         }
    40         return false;
    41     }
    42 
    43     /**
    44      * 解锁并释放redis连接
    45      */
    46     public boolean release(Jedis jedis,String key,String value) {
    47         try {
    48             String[] params1 = new String[] { key, value };
    49             if("1".equals(jedis.eval(lua, 1, params1))){
    50                 jedis.close();
    51                 return true;
    52             }
    53             return false;
    54         } catch (Exception e) {
    55             return false;
    56         }
    57     }
    58 }

    测试代码:

    package cn.wywk.yac.redistest;
    import cn.wywk.yac.comm.redis.RedisLock;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    public class RedisLockTest implements Runnable{
        
         private static JedisPool pool = null;  
         
         private String xianName="";
             
         public RedisLockTest(){}
         
         public RedisLockTest(String name){
             xianName=name;
         }
         
        /** 
         * 构建redis连接池 
         *  
         * @param ip 
         * @param port 
         * @return JedisPool 
         */  
        public static JedisPool getPool() {  
            if (pool == null) {  
                JedisPoolConfig config = new JedisPoolConfig();  
                //控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;  
                //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。  
                config.setMaxIdle(500);  
                //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。  
                config.setMaxIdle(5);  
                //表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;  
                config.setMaxWaitMillis(1000 * 100);  
                //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;  
                config.setTestOnBorrow(true);  
                pool = new JedisPool(config, "127.0.0.1",6379);  
            }  
            return pool;  
        }  
        
        @Override
        public void run() {
            RedisLock jedisLock = new RedisLock();  
            try {  
                if (jedisLock.acquire(jedis,"aa","123231",10,6000)) { // 启用锁  
                    System.out.println("线程名:"+xianName+"获得锁");
                    //执行业务逻辑
                    //....
                    //释放锁
                    Thread.sleep(5000);
                    System.out.println("---------------线程名:"+xianName+"开始释放锁");
                    jedisLock.release(jedis,"aa","123231");
                } else {  
                    System.out.println("ERROR-线程名:"+xianName+"获取锁失败");
                    //返回给连接池
                    jedis.close();
                }  
            } catch (Exception e) {  
                // 分布式锁异常  
                e.printStackTrace();
            } finally {  
    //            if (jedis != null) {  
    //                try {
    //                    
    //                } catch (Exception e) {
    //                    e.printStackTrace();
    //                }  
    //            }  
            }
        }
            
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i <40; i++) {
                RedisLockTest test=new RedisLockTest("A线程"+i);
                new Thread(test).start();
    //            RedisLockTest test2=new RedisLockTest("B线程"+i);
    //            new Thread(test2).start();
            }
        }
    }

    释放锁的时候:

    不使用 DEL 命令来释放锁,而是发送一个 Lua 脚本,这个脚本只在客户端传入的值和键的口令串相匹配时,才对键进行删除。 这两个改动可以防止持有过期锁的客户端误删现有锁的情况出现。

  • 相关阅读:
    python机器学习-数据集划分
    python机器学习-数据集的使用
    char类型标识字段
    CharIndex的用法
    临时表和表变量性能差别
    建索引数据优化实例
    公用表达式的结果集不要子查询然后连接查询
    Oracle SQL in 超过1000 的解决方案
    Oracle 11g大数据量表快速增加列
    SQL Server表值函数
  • 原文地址:https://www.cnblogs.com/wuxinyu/p/6410396.html
Copyright © 2011-2022 走看看