关闭

Redis 分布式锁实现

标签: 分布式memcached
275人阅读 评论(0) 收藏 举报
分类:

原文地址:http://blog.csdn.net/zhu_tianwei/article/details/44927331



Redis是一个key-value存储系统。和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

    下面我们以redis为基础设计分布式锁。

1.锁接口定义

  1. package cn.slimsmart.redis.demo.lock;  
  2.   
  3. import java.util.List;  
  4. import java.util.concurrent.TimeUnit;  
  5.   
  6. /** 
  7.  *锁接口定义 
  8.  */  
  9. public interface IRedisLockHandler {  
  10.       
  11.     /** 
  12.      * 获取锁  如果锁可用   立即返回true,  否则返回false  
  13.      * @param key 
  14.      * @return 
  15.      */  
  16.     boolean tryLock(String key);  
  17.     /** 
  18.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false  
  19.      * @param key 
  20.      * @param timeout 
  21.      * @param unit 
  22.      * @return 
  23.      */  
  24.     boolean tryLock(String key, long timeout, TimeUnit unit);  
  25.       
  26.     /** 
  27.      * 如果锁空闲立即返回   获取失败 一直等待  
  28.      * @param key 
  29.      */  
  30.     void lock(String key);  
  31.       
  32.     /** 
  33.      * 批量获取锁  如果全部获取   立即返回true, 部分获取失败 返回false  
  34.      * @param keyList 
  35.      * @return 
  36.      */  
  37.     boolean tryLock(List<String> keyList);  
  38.       
  39.     /** 
  40.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false  
  41.      * @param keyList 
  42.      * @param timeout 
  43.      * @param unit 
  44.      * @return 
  45.      */  
  46.     boolean tryLock(List<String> keyList, long timeout, TimeUnit unit);  
  47.       
  48.       
  49.     /** 
  50.      * 释放锁 
  51.      * @param key 
  52.      */  
  53.     void unLock(String key);  
  54.     /** 
  55.      * 批量释放锁  
  56.      * @param keyList 
  57.      */  
  58.     void unLock(List<String> keyList);  
  59. }  
2.锁接口实现
  1. package cn.slimsmart.redis.demo.lock;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.concurrent.CopyOnWriteArrayList;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10.   
  11. import redis.clients.jedis.Jedis;  
  12. import redis.clients.jedis.JedisPool;  
  13. import redis.clients.jedis.Pipeline;  
  14. import redis.clients.jedis.exceptions.JedisConnectionException;  
  15.   
  16. /** 
  17.  * redis 分布式锁实现 
  18.  * 
  19.  */  
  20. public class RedisLockHandler implements IRedisLockHandler {  
  21.   
  22.     private static final Logger LOGGER = LoggerFactory.getLogger(RedisLockHandler.class);  
  23.   
  24.     // 单个锁有效期  
  25.     private static final int DEFAULT_SINGLE_EXPIRE_TIME = 30;  
  26.     // 批量锁有效期  
  27.     private static final int DEFAULT_BATCH_EXPIRE_TIME = 60;  
  28.   
  29.     private final JedisPool jedisPool;  
  30.   
  31.     /** 
  32.      * 构造 
  33.      */  
  34.     public RedisLockHandler(JedisPool jedisPool) {  
  35.         this.jedisPool = jedisPool;  
  36.     }  
  37.       
  38.     /** 
  39.      * 获取锁 如果锁可用 立即返回true, 否则返回false,不等待 
  40.      *  
  41.      * @return 
  42.      */  
  43.     @Override  
  44.     public boolean tryLock(String key) {  
  45.         return tryLock(key, 0L, null);  
  46.     }  
  47.   
  48.     /** 
  49.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false 
  50.      *  
  51.      * @param timeout 
  52.      * @param unit 
  53.      * @return 
  54.      */  
  55.     @Override  
  56.     public boolean tryLock(String key, long timeout, TimeUnit unit) {  
  57.         Jedis jedis = null;  
  58.         try {  
  59.             jedis = getResource();  
  60.             //系统计时器的当前值,以毫微秒为单位。  
  61.             long nano = System.nanoTime();  
  62.             do {  
  63.                 LOGGER.debug("try lock key: " + key);  
  64.                 //将 key 的值设为 value 1成功  0失败  
  65.                 Long i = jedis.setnx(key, key);  
  66.                 if (i == 1) {  
  67.                     //设置过期时间  
  68.                     jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
  69.                     LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
  70.                     //成功获取锁,返回true  
  71.                     return Boolean.TRUE;  
  72.                 } else { // 存在锁,循环等待锁  
  73.                     if (LOGGER.isDebugEnabled()) {  
  74.                         String desc = jedis.get(key);  
  75.                         LOGGER.debug("key: " + key + " locked by another business:" + desc);  
  76.                     }  
  77.                 }  
  78.                 if (timeout <= 0) {  
  79.                     //没有设置超时时间,直接退出等待  
  80.                     break;  
  81.                 }  
  82.                 Thread.sleep(300);  
  83.             } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
  84.             return Boolean.FALSE;  
  85.         } catch (JedisConnectionException je) {  
  86.             LOGGER.error(je.getMessage(), je);  
  87.             //释放资源  
  88.             returnBrokenResource(jedis);  
  89.         } catch (Exception e) {  
  90.             LOGGER.error(e.getMessage(), e);  
  91.         } finally {  
  92.             returnResource(jedis);  
  93.         }  
  94.         return Boolean.FALSE;  
  95.     }  
  96.   
  97.     /** 
  98.      * 如果锁空闲立即返回 获取失败 一直等待 
  99.      */  
  100.     @Override  
  101.     public void lock(String key) {  
  102.         Jedis jedis = null;  
  103.         try {  
  104.             jedis = getResource();  
  105.             do {  
  106.                 LOGGER.debug("lock key: " + key);  
  107.                 Long i = jedis.setnx(key, key);  
  108.                 if (i == 1) {  
  109.                     jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
  110.                     LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
  111.                     return;  
  112.                 } else {  
  113.                     if (LOGGER.isDebugEnabled()) {  
  114.                         String desc = jedis.get(key);  
  115.                         LOGGER.debug("key: " + key + " locked by another business:" + desc);  
  116.                     }  
  117.                 }  
  118.                 Thread.sleep(300);  
  119.             } while (true);  
  120.         } catch (JedisConnectionException je) {  
  121.             LOGGER.error(je.getMessage(), je);  
  122.             returnBrokenResource(jedis);  
  123.         } catch (Exception e) {  
  124.             LOGGER.error(e.getMessage(), e);  
  125.         } finally {  
  126.             returnResource(jedis);  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * 释放锁 
  132.      */  
  133.     @Override  
  134.     public void unLock(String key) {  
  135.         List<String> list = new ArrayList<String>();  
  136.         list.add(key);  
  137.         unLock(list);  
  138.     }  
  139.   
  140.     /** 
  141.      * 批量获取锁 如果全部获取 立即返回true, 部分获取失败 返回false 
  142.      *  
  143.      * @return 
  144.      */  
  145.     @Override  
  146.     public boolean tryLock(List<String> keyList) {  
  147.         return tryLock(keyList, 0L, null);  
  148.     }  
  149.   
  150.     /** 
  151.      * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false 
  152.      *  
  153.      * @param timeout 
  154.      * @param unit 
  155.      * @return 
  156.      */  
  157.     @Override  
  158.     public boolean tryLock(List<String> keyList, long timeout, TimeUnit unit) {  
  159.         Jedis jedis = null;  
  160.         try {  
  161.             //需要的锁  
  162.             List<String> needLocking = new CopyOnWriteArrayList<String>();  
  163.             //得到的锁  
  164.             List<String> locked = new CopyOnWriteArrayList<String>();  
  165.             jedis = getResource();  
  166.             long nano = System.nanoTime();  
  167.             do {  
  168.                 // 构建pipeline,批量提交  
  169.                 Pipeline pipeline = jedis.pipelined();  
  170.                 for (String key : keyList) {  
  171.                     needLocking.add(key);  
  172.                     pipeline.setnx(key, key);  
  173.                 }  
  174.                 LOGGER.debug("try lock keys: " + needLocking);  
  175.                 // 提交redis执行计数,批量处理完成返回  
  176.                 List<Object> results = pipeline.syncAndReturnAll();  
  177.                 for (int i = 0; i < results.size(); ++i) {  
  178.                     Long result = (Long) results.get(i);  
  179.                     String key = needLocking.get(i);  
  180.                     if (result == 1) { // setnx成功,获得锁  
  181.                         jedis.expire(key, DEFAULT_BATCH_EXPIRE_TIME);  
  182.                         locked.add(key);  
  183.                     }  
  184.                 }  
  185.                 needLocking.removeAll(locked); // 已锁定资源去除  
  186.   
  187.                 if (needLocking.size() == 0) { //成功获取全部的锁  
  188.                     return true;  
  189.                 } else {  
  190.                     // 部分资源未能锁住  
  191.                     LOGGER.debug("keys: " + needLocking + " locked by another business:");  
  192.                 }  
  193.   
  194.                 if (timeout == 0) {  
  195.                     break;  
  196.                 }  
  197.                 Thread.sleep(500);  
  198.             } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
  199.   
  200.             // 得不到锁,释放锁定的部分对象,并返回失败  
  201.             if (locked.size() > 0) {  
  202.                 jedis.del(locked.toArray(new String[0]));  
  203.             }  
  204.             return false;  
  205.         } catch (JedisConnectionException je) {  
  206.             LOGGER.error(je.getMessage(), je);  
  207.             returnBrokenResource(jedis);  
  208.         } catch (Exception e) {  
  209.             LOGGER.error(e.getMessage(), e);  
  210.         } finally {  
  211.             returnResource(jedis);  
  212.         }  
  213.         return true;  
  214.     }  
  215.   
  216.     /** 
  217.      * 批量释放锁 
  218.      */  
  219.     @Override  
  220.     public void unLock(List<String> keyList) {  
  221.         List<String> keys = new CopyOnWriteArrayList<String>();  
  222.         for (String key : keyList) {  
  223.             keys.add(key);  
  224.         }  
  225.         Jedis jedis = null;  
  226.         try {  
  227.             jedis = getResource();  
  228.             jedis.del(keys.toArray(new String[0]));  
  229.             LOGGER.debug("release lock, keys :" + keys);  
  230.         } catch (JedisConnectionException je) {  
  231.             LOGGER.error(je.getMessage(), je);  
  232.             returnBrokenResource(jedis);  
  233.         } catch (Exception e) {  
  234.             LOGGER.error(e.getMessage(), e);  
  235.         } finally {  
  236.             returnResource(jedis);  
  237.         }  
  238.     }  
  239.   
  240.     /** 
  241.      * 获取redis客户端 
  242.      * @return 
  243.      */  
  244.     private Jedis getResource() {  
  245.         return jedisPool.getResource();  
  246.     }  
  247.   
  248.     /** 
  249.      * 销毁连接 
  250.      * @param jedis 
  251.      */  
  252.     private void returnBrokenResource(Jedis jedis) {  
  253.         if (jedis == null) {  
  254.             return;  
  255.         }  
  256.         try {  
  257.             //中断链接  
  258.             jedisPool.returnBrokenResource(jedis);  
  259.         } catch (Exception e) {  
  260.             LOGGER.error(e.getMessage(), e);  
  261.         }  
  262.     }  
  263.   
  264.     /** 
  265.      * 重新初始化对象 
  266.      * @param jedis 
  267.      */  
  268.     private void returnResource(Jedis jedis) {  
  269.         if (jedis == null) {  
  270.             return;  
  271.         }  
  272.         try {  
  273.             jedisPool.returnResource(jedis);  
  274.         } catch (Exception e) {  
  275.             LOGGER.error(e.getMessage(), e);  
  276.         }  
  277.     }  
  278. }  
3.分布式锁测试
  1. package cn.slimsmart.redis.demo.lock;  
  2.   
  3. import java.util.concurrent.TimeUnit;  
  4.   
  5. import redis.clients.jedis.JedisPool;  
  6. import redis.clients.jedis.JedisPoolConfig;  
  7.   
  8. public class RedisLockMain {  
  9.       
  10.     public static void main(String[] args) {  
  11.         //创建jedis池配置实例    
  12.         JedisPoolConfig config = new JedisPoolConfig();     
  13.         //设置池配置项值    
  14.         config.setMaxTotal(1024);      
  15.         config.setMaxIdle(200);      
  16.         config.setMaxWaitMillis(1000);      
  17.         config.setTestOnBorrow(true);      
  18.         config.setTestOnReturn(true);   
  19.           
  20.         //根据配置实例化jedis池    
  21.         JedisPool  pool = new JedisPool(config,"192.168.100.205"6379);    
  22.         IRedisLockHandler lock = new RedisLockHandler(pool);  
  23.         if(lock.tryLock("abcd",20,TimeUnit.SECONDS)){  
  24.             System.out.println(" get lock ...");  
  25.         }else{  
  26.             System.out.println(" not get lock ...");  
  27.         }  
  28.        lock.unLock("abcd");  
  29.   
  30.     }  
  31.   
  32. }  
关于通过spring aop实现分布式锁,请参考:http://blog.csdn.net/michaelzhaozero/article/details/23746059
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:363592次
    • 积分:4963
    • 等级:
    • 排名:第5971名
    • 原创:49篇
    • 转载:487篇
    • 译文:0篇
    • 评论:22条