基于redis setnx的简易分布式锁
2017-07-06 12:43
169 查看
(此文章实现有误,还未修复)
锁的原理, 就是设置个flag,记录下是否正被使用,可重入锁再判断下是否是自己在使用.
这个flag,必须保证所有资源用的是同一个.
synchronized关键字,lock类等, 可以保证此flag在单个jvm中唯一, 但是有多个jvm(集群,分布式)时候,就没办法保证了.
这时候需要用一个 跨jvm唯一的flag. 也就是常说的分布式锁了.
分布式锁的实现也有很多种. 基于redis,zookeeper等等, 也可以基于数据库.
基于redis的,也有setnx,incr等操作的.
本着简单,实用的原则, 写了一个简单的类,主要支持以下功能:
1.所有加锁操作都需要有时间限制,不能无限锁定
2.提供获取失败重试机制
3.释放锁时,保证释放的锁是自己获取到的
下面是代码,用到了jedis的jar, redis的连接是和spring整合配置的.这里就不列了. 所以不能直接运行
重点是LockUtil类.(锁存在,检查锁的剩余时间,然后重试,这里没有扣减此过程消耗的时间.所以极端情况会出现超出tryLock的timeOut设置而获取到锁)
使用示例如下
欢迎讨论拍砖交流
锁的原理, 就是设置个flag,记录下是否正被使用,可重入锁再判断下是否是自己在使用.
这个flag,必须保证所有资源用的是同一个.
synchronized关键字,lock类等, 可以保证此flag在单个jvm中唯一, 但是有多个jvm(集群,分布式)时候,就没办法保证了.
这时候需要用一个 跨jvm唯一的flag. 也就是常说的分布式锁了.
分布式锁的实现也有很多种. 基于redis,zookeeper等等, 也可以基于数据库.
基于redis的,也有setnx,incr等操作的.
本着简单,实用的原则, 写了一个简单的类,主要支持以下功能:
1.所有加锁操作都需要有时间限制,不能无限锁定
2.提供获取失败重试机制
3.释放锁时,保证释放的锁是自己获取到的
下面是代码,用到了jedis的jar, redis的连接是和spring整合配置的.这里就不列了. 所以不能直接运行
重点是LockUtil类.(锁存在,检查锁的剩余时间,然后重试,这里没有扣减此过程消耗的时间.所以极端情况会出现超出tryLock的timeOut设置而获取到锁)
import java.util.Objects; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.zhidian.common.util.SpringContextUtil; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.exceptions.JedisException; /** * 基于redis setnx的 分布式锁 实, 前提是所有的锁都要有锁定时间. * 获取锁的时候,需要指定value,在unlock的时候,会根据value判断是否remove * * @author: qq315737546 */ public class LockUtil { private static Log logger = LogFactory.getLog(LockUtil.class); private static final String LOCK_PREFIX = "LOCK"; private static final Integer DEFAULT_LOCK_TIME = 20;// 默认锁定时间秒 private static final Long DEFAULT_SLEEP_TIME = 100L;// 默认sleep时间,100毫秒 /** * 单台服务器可直接用系统时间,多台服务器可用redis.time() * * @return * @Author: qq315737546 */ public static String getCurTime() { return String.valueOf(System.currentTimeMillis()); } /** * 获取锁,如果失败,自动重试 * * @param key * @param value * @return * @Author: qq315737546 */ public static void lock(String key, String value) { lock(key, value, DEFAULT_LOCK_TIME); } /** * 获取锁,如果失败,自动重试 * * @param key * @param value * @param lockTime * 锁定时间 * @return * @Author: qq315737546 */ public static void lock(String key, String value, int lockTime) { lock(key, value, lockTime, true); } /** * * @param key * @param value * @param lockTime * 锁定时间 * @param reTry * 是否重试 * @return * @Author: wangxingfei */ private static boolean lock(String key, String value, int lockTime, boolean reTry) { return lock(key, value, lockTime, reTry, 0, false, 0); } /** * 获取锁,如果失败,直接返回false * * @param key * @param value * @return * @Author: qq315737546 */ public static boolean tryLock(String key, String value) { return tryLock(key, value, DEFAULT_LOCK_TIME); } /** * 获取锁,如果失败,直接返回false * * @param key * @param value * @param lockTime * 锁定时间 * @return * @Author: qq315737546 */ public static boolean tryLock(String key, String value, int lockTime) { return lock(key, value, lockTime, false); } /** * 尝试获取锁,如果获取失败,重试,直到成功或超出指定时间 * * @param key * @param value * @param lockTime * @param timeOutMillis * 获取锁超时时间 (毫秒) * * @return * @Author: qq315737546 */ public static boolean tryLock(String key, String value, int lockTime, long timeOutMillis) { return lock(key, value, lockTime, true, 0, true, timeOutMillis); } /** * 释放锁,key对应的value于参数value一致,才删除key * * @param key * @param value * @Author: qq315737546 */ public static void unlock(String key, String value) { String fullKey = getFullKey(key); String existValue = JedisUtil.getObject(fullKey); if (Objects.equals(value, existValue)) { logger.info("unlock success ; key:" + key + ",value:" + value); JedisUtil.remove(fullKey); } else { logger.info("unlock failed ; key:" + key + ",value:" + value + ",existValue:" + existValue); } } /** * 获取锁 * * @param key * @param value * @param lockTime * 锁定时间 * @param reTry * 失败是否重试 * @param curTryTime * 当前尝试次数 * @param needTimeOut * 是否需要判断超时时间 * @param timeOutMillis * 尝试超时时间(毫秒) * @return * @Author: qq315737546 */ private static boolean lock(String key, String value, int lockTime, boolean reTry, int curTryTime, boolean needTimeOut, long timeOutMillis) { logger.info(Thread.currentThread().getName() + ",lock come in ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:" + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:" + timeOutMillis); curTryTime++; String fullKey = getFullKey(key); long result = JedisUtil.setnx(fullKey, value); // 获取成功,直接返回 if (result > 0) { logger.info("lock success ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:" + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:" + timeOutMillis); JedisUtil.expire(fullKey, lockTime); return true; } // 如果不成功,判断现在的key的有效期,如果是无限期,则删除key,并重试 long expire = JedisUtil.ttl(fullKey); if (expire < 0) { if (expire == -1) { JedisUtil.remove(fullKey); logger.info("remove key ; key:" + key + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:" + timeOutMillis); } else { logger.info("ttl key ; key:" + key + " is return " + expire); } return lock(key, value, lockTime, reTry, curTryTime, needTimeOut, timeOutMillis); } // 获取失败,不需要重试,直接返回 if (!reTry) { logger.info("lock failed ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:" + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:" + timeOutMillis); return false; } // 获取失败, 且已超时,返回 if (needTimeOut && timeOutMillis <= 0) { logger.info("lock failed ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:" + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:" + timeOutMillis); return false; } // 获取sleep时间 long sleepMillis = DEFAULT_SLEEP_TIME; if (needTimeOut) { timeOutMillis = timeOutMillis - DEFAULT_SLEEP_TIME; if (timeOutMillis < DEFAULT_SLEEP_TIME) { sleepMillis = timeOutMillis; } } // sleep后重新获取锁 try { Thread.sleep(sleepMillis); } catch (InterruptedException e) { logger.error("lock sleep errro ; key:" + key + ",value:" + value, e); } if (curTryTime > 100) { logger.warn("lock warning ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:" + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:" + timeOutMillis); } return lock(key, value, lockTime, reTry, curTryTime, needTimeOut, timeOutMillis); } private static String getFullKey(String key) { return LOCK_PREFIX + ":" + key; } } class JedisUtil { private static Log logger = LogFactory.getLog(JedisUtil.class); protected static JedisPool jedisPool = (JedisPool) SpringContextUtil.getBean("jedisPool");; /** * 获取redis操作实例(不必加锁) * * @return jedis */ protected static Jedis getJedis() throws JedisException { Jedis jedis = null; try { jedis = jedisPool.getResource(); } catch (JedisException e) { logger.warn("failed:jedisPool getResource.", e); if (jedis != null) { jedisPool.returnBrokenResource(jedis); } throw e; } return jedis; } protected static void release(Jedis jedis, boolean isBroken) { if (jedis != null) { if (isBroken) { jedisPool.returnBrokenResource(jedis); } else { jedisPool.returnResource(jedis); } } } /** * set if not exists */ public static long setnx(String key, String value) { Jedis jedis = null; boolean isBroken = false; long result = 0L; try { jedis = getJedis(); result = jedis.setnx(key, value); } catch (Exception e) { isBroken = true; logger.error("JedisDao::setnx: key: " + key + ",value:" + value + " message: " + e.getMessage(), e); } finally { release(jedis, isBroken); } return result; } /** * 设置过期时间 */ public static void expire(String fullKey, int lockTime) { Jedis jedis = null; boolean isBroken = false; try { jedis = getJedis(); jedis.expire(fullKey, lockTime); } catch (Exception e) { isBroken = true; logger.error("JedisDao:expire: key: " + fullKey + ",lockTime:" + lockTime + " message: " + e.getMessage(),e); } finally { release(jedis, isBroken); } } /** * 查看key的有效期 * * @param fullKey * @return 如果key不存在或者已过期,返回 -2 ;如果key没有设置过期时间(永久有效),返回 -1 ,否则返回有效期(单位秒) */ public static long ttl(String fullKey) { Jedis jedis = null; boolean isBroken = false; Long result = 0L; try { jedis = getJedis(); result = jedis.ttl(fullKey); } catch (Exception e) { isBroken = true; logger.error("JedisDao: ttl : key: " + fullKey + " message: " + e.getMessage(), e); } finally { release(jedis, isBroken); } return result; } public static void remove(String key) { Jedis jedis = null; boolean isBroken = false; try { jedis = getJedis(); jedis.del(key); } catch (Exception e) { isBroken = true; logger.error("JedisDao::remove: key: " + key + " message: " + e.getMessage(), e); } finally { release(jedis, isBroken); } } public static String getObject(String key) { Jedis jedis = null; boolean isBroken = false; try { jedis = getJedis(); String value = jedis.get(key); return value; } catch (Exception e) { isBroken = true; logger.error("JedisDao::getObject: key: " + key + " message: " + e.getMessage(), e); } finally { release(jedis, isBroken); } return null; } }
使用示例如下
String key = "testLock"; String value = LockUtil.getCurTime(); try{ LockUtil.lock(key, value); }finally{ LockUtil.unlock(key, value); } boolean lock = LockUtil.tryLock(key, value); if(lock){ try{ //TODO }finally{ LockUtil.unlock(key, value); } }
欢迎讨论拍砖交流
相关文章推荐
- 基于Redis setNX 实现分布式锁
- 基于 xNet 通信框架搭建的简易分布式系统 Demo ~~
- 基于Redis实现的分布式锁
- 分布式锁实现方案1、基于Redis的SETNX操作实现的分布式锁
- <五>基于Fourinone实现分布式锁指南和demo
- 详解使用Redis SETNX 命令实现分布式锁
- 基于mini2440的简易bootloader
- 《设计模式--基于C#的工程化实现及扩展》补充 Security Design Pattern 系列 1 公钥体系与分布式环境要求
- 基于redis集群实现的分布式锁,可用于秒杀商品的库存数量管理,有测试代码(何志雄)
- PingCAP唐刘:基于Raft构建分布式系统TiKV
- 基于java的分布式爬虫
- 基于Bootstrap与jQuery-Validate的个人简易封装
- 【分布式缓存】——-基于redis分布式缓存的实现
- 基于Spring+SpringMVC+Mybatis分布式敏捷开发系统架构---权限管理系统
- 基于ASP.NET WEB API实现分布式数据访问中间层(提供对数据库的CRUD)
- 阿里集团云梯分布式平台:基于Hadoop的内部海量数据服务平台
- 架构设计实践:基于WCF大型分布式系统
- 基于centos6.5 hadoop 伪分布式安装
- 基于zookeeper简单实现分布式锁