利用redis实现分布式锁详解大数据

SETNX  并不难完美实现(不带过期时间),SETNX 实现锁有陷阱需谨慎

SETEX  复写,带过期时间(原子)

 

分布式锁工具    

	private static Logger logger = Logger.getLogger(LockUtils.class); 
       /** 
	 * 最长时间锁为1天 
	 */ 
	private final static int maxExpireTime = 24 * 60 * 60; 
 
	/** 
	 * 系统时间偏移量15秒,服务器间的系统时间差不可以超过15秒,避免由于时间差造成错误的解锁 
	 */ 
	private final static int offsetTime = 15; 
 
	/** 
	 * 锁只是为了解决小概率事件,最好的方式是不用,从设计上避免分布式锁 
	 *  
	 * @param key 
	 *            key 
	 * @param value 
	 * @param waitTime 
	 *            秒 - 最大等待时间,如果还无法获取,则直接失败 
	 * @param expire 
	 *            秒- 锁生命周期时间 
	 * @return true 成功 false失败 
	 * @throws Exception 
	 */ 
	public static boolean Lock(String key, String value, int waitTime, int expire) { 
 
		long start = System.currentTimeMillis(); 
		String lock_key = key + "_lock"; 
		logger.info("开始获取分布式锁 key:" + key + " lock_key:" + lock_key + " value:" + value); 
 
		do { 
			try { 
				Thread.sleep(1); 
				long ret = CacheUtils.Setnx(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key, System.currentTimeMillis() + "$T$" + value, 
				                            (expire > maxExpireTime) ? maxExpireTime : expire); 
				if (ret == 1) { 
					logger.info("成功获得分布式锁 key:" + key + " value:" + value); 
					return Boolean.TRUE; 
				} else { // 存在锁,并对死锁进行修复 
					String desc = CacheUtils.GSetnx(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key); 
 
					// 首次锁检测 
					if (desc.indexOf("$T$") > 0) { 
						// 上次锁时间 
						long lastLockTime = NumberUtils.toLong(desc.split("[$T$]")[0]); 
						// 明确死锁,利用Setex复写,再次设定一个合理的解锁时间让系统正常解锁 
						if (System.currentTimeMillis() - lastLockTime > (expire + offsetTime) * 1000) { 
							// 原子操作,只需要一次,【任然会发生小概率事件,多个服务同时发现死锁同时执行此行代码(并发), 
							// 为什么设置解锁时间为expire(而不是更小的时间),防止在解锁发送错乱造成新锁解锁】 
							CacheUtils.Setex(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key, value, expire); 
							logger.warn("发现死锁【" + expire + "秒后解锁】key:" + key + " desc:" + desc); 
						} else { 
							logger.info("当前锁key:" + key + " desc:" + desc); 
						} 
					} else { 
						logger.warn("死锁解锁中key:" + key + " desc:" + desc); 
					} 
 
				} 
				if (waitTime == 0) { 
					break; 
				} 
				Thread.sleep(500); 
			} 
			catch (Exception ex) { 
				logger.error(Trace.GetTraceStackDetails("获取锁失败", ex)); 
			} 
		} 
		while ((System.currentTimeMillis() - start) < waitTime * 1000); 
		logger.warn("获取分布式锁失败 key:" + key + " value:" + value); 
		return Boolean.FALSE; 
	} 
 
	/** 
	 * 解锁 
	 *  
	 * @param key 
	 * @return 
	 * @throws Exception 
	 */ 
	public static boolean UnLock(String key) { 
		String lock_key = key + "_lock"; 
		try { 
			CacheUtils.Del(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key); 
		} 
		catch (Exception ex) { 
			logger.error(Trace.GetTraceStackDetails("解锁锁失败key:" + key + " lock_key:" + lock_key, ex)); 
		} 
		return Boolean.FALSE; 
	} 

redis操作分装部分    

	@Override 
	public Long Setnx(String key, String value, int expireTime) throws Exception { 
		ShardedJedis jedis = null; 
 
		try { 
			jedis = pool.getResource(); 
 
			Long ret = jedis.setnx(key, value); 
			if (ret == 1 && expireTime > 0) { 
				jedis.expire(key, expireTime); 
			} 
			return ret; 
		} 
		catch (Exception e) { 
			throw e; 
		} 
		finally { 
			if (pool != null && jedis != null) { 
				pool.returnResourceObject(jedis); 
			} 
		} 
	} 
 
	@Override 
	public String Setex(String key, String value, int expireTime) throws Exception { 
		ShardedJedis jedis = null; 
 
		try { 
			jedis = pool.getResource(); 
			  
 
			String ret = jedis.setex(key, expireTime, value); 
			return ret; 
		} 
		catch (Exception e) { 
			throw e; 
		} 
		finally { 
			if (pool != null && jedis != null) { 
				pool.returnResourceObject(jedis); 
			} 
		} 
 
	} 
 
	@Override 
	public String GSetnx(String key) throws Exception { 
		ShardedJedis jedis = null; 
 
		try { 
			jedis = pool.getResource(); 
			return jedis.get(key); 
		} 
		catch (Exception e) { 
			throw e; 
		} 
		finally { 
			if (pool != null && jedis != null) { 
				pool.returnResourceObject(jedis); 
			} 
		} 
	}

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/8974.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论