redis与zk实现分布式锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25868207/article/details/83108143
  1. 概述

分布式锁,如果你有多个机器在访问同一个共享资源,

那么这个时候,如果你需要加个锁,让多个分布式的机器在访问共享资源的时候串行起来

那么这个时候,那个锁,多个不同机器上的服务共享的锁,就是分布式锁

分布式锁当然有很多种不同的实现方案,redis分布式锁,zookeeper分布式锁

  1. 对比

数据库锁:

优点:直接使用数据库,使用简单。

缺点:分布式系统大多数瓶颈都在数据库,使用数据库锁会增加数据库负担。

缓存锁:

优点:性能高,实现起来较为方便,在允许偶发的锁失效情况,不影响系统正常使用,建议采用缓存锁。

缺点:通过锁超时机制不是十分可靠,当线程获得锁后,处理时间过长导致锁超时,就失效了锁的作用。

zookeeper锁:

优点:不依靠超时时间释放锁;可靠性高;系统要求高可靠性时,建议采用zookeeper锁。

缺点:性能比不上缓存锁,因为要频繁的创建节点删除节点。
  1. zk实现

     /**
      * ZkSession
      * 
      * @author kris
      * 
      */
     public class ZkSession {
    
     private static Logger log = LoggerFactory.getLogger(ZkSession.class);
    
     private static RetryPolicy retryPolicy;
     
     private InterProcessMutex mutex ;
     private CuratorFramework client ;
    
     public ZkSession() {
     	try {
     		//初试时间为1s 重试3次
     		retryPolicy = new ExponentialBackoffRetry(1000, 3);
     		//"192.168.0.132:2181,192.168.0.132:2182,192.168.0.132:2183"
     		client = CuratorFrameworkFactory.newClient(XxlConfClient.get("cache.host", ""), retryPolicy);
     		//创建zookeeper的客户端
     		client.start();
     	} catch (Exception e) {
     		e.printStackTrace();
     	}
     }
    
     /**
      * 获取分布式锁
      * 
      * @param path
      *            /curator/lock
      */
     public void acquireLock(String path) {
     	boolean flag = false;
     	try {
     		mutex = new InterProcessMutex(client, path);
     		int count = 0;
     		
     		while (true) {
     			try {
     				// 尝试获取锁,最多等待5秒
     				flag = mutex.acquire(2, TimeUnit.SECONDS);
     				Thread currentThread = Thread.currentThread();
     				if (flag) {
     					log.info("线程" + currentThread.getId() + "获取锁成功!  path:"+path);
     				} else {
     					log.info("线程" + currentThread.getId() + "获取锁失败");
     					count++;
     					log.info("the " + count + " times try to acquire lock for " + path + "......");
     					continue;
     				}
     			} catch (Exception e2) {
     				e2.printStackTrace();
     			}
     			break;
     		}
    
     	} catch (Exception e) {
     		e.printStackTrace();
     	} 
     }
     
     /**
      * 释放锁
      */
     public void releaseLock(){
     	try {
     		if (null != mutex) {
     			mutex.release();
     		}
    
     		if (null != client) {
     			client.close();
     		}
     	} catch (Exception e) {
     		e.printStackTrace();
     	}
     }
    

    }

3.1 使用

//创建临时节点路径
String path = "/curator/lock";

ZkSession lock = new ZkSession().acquireLock(lock);

//...执行业务需求
Thread.sleep(3000);

lock.releaseLock();
  1. redis实现

我这里是用codis的jodis实现的

public class JodisUtils {

	static {
		JedisPoolConfig config = null;
		try {
			config = initConfig();
			if (!closeFlag) {
				LOG.info("启动参数 host: " + XxlConfClient.get("cache.host", "") + " path: " + XxlConfClient.get("cache.path", ""));
				pool = RoundRobinJedisPool.create().poolConfig(config).curatorClient(XxlConfClient.get("cache.host", ""), 30000)
						.zkProxyDir(XxlConfClient.get("cache.path", "")).build();
			}
		} catch (Exception e) {
			LOG.info("初始化错误,关掉缓存" + e.getMessage(), e);
		}
	}
	private static JodisUtils ins;

	public static JodisUtils getInstance() {
		if (ins == null) {
			ins = new JodisUtils();
		}
		return ins;
	}

	/**
	 * 原子获取锁
	 * @param key
	 * @param value
	 * @param expireMillis
	 * @return
	 */
	public boolean acquireLock(String key, String value, int expireMillis) {
		try {
			Jedis jedis = pool.getResource();
			try {
				// nx = not exist, px= 单位是毫秒
				boolean flag = false;
				String result = jedis.set(key, value, "NX", "PX", expireMillis);
				if (result != null && result.equalsIgnoreCase("OK")) {
					flag = true;
				}
				return flag;
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
				return false;
			} finally {
				jedis.close();
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return false;
		}
	}

	/**
	 * 执行lua脚本释放锁(删除)
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean releaseLock(String key, String value) {
		try {
			Jedis jedis = pool.getResource();
			try {
				boolean flag = false;
				String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] " + "then " + " return redis.call(\"del\",KEYS[1]) " + "else " + " return 0 " + "end ";
				Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));
				if (Objects.equals(UNLOCK_SUCCESS, result)) {
					flag = true;
				}
				return flag;
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			} finally {
				//jedis.unwatch();
				jedis.close();
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			return false;
		}
	}

}

4.1 使用

String key = "testLock";
String uuid = System.currentTimeMillis()+UUID.randomUUID().toString();
	
int count = 0;
	
while (true) {
	try {
		boolean acquireLock = JodisUtils.getInstance().acquireLock(key, uuid, 30000);
		Thread currentThread = Thread.currentThread();
			
			
		if (acquireLock) {
			LOG.info("线程" + currentThread.getId() + "获取 锁 成功!  key:"+key);
		} else {
			LOG.info("线程" + currentThread.getId() + "获取 锁 失败");
			
			count++;
			Thread.sleep(100);
			LOG.info("the " + count + " times try to acquire lock for " + key + "......");
			continue;
		}
		
		//执行业务需求
		Thread.sleep(500);
		boolean releaseLock = JodisUtils.getInstance().releaseLock(key, uuid);
			
		if (releaseLock) {
			LOG.info("线程" + currentThread.getId() + "释放 锁 成功!  key:"+key);
			break;
		}
	} catch (Exception e2) {
		e2.printStackTrace();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_25868207/article/details/83108143