redis与zk实现分布式锁

  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 seconds) {
	try {
		while (true) {
			try {
				
				int count = 0;
				boolean acquireLock = JodisUtils.getInstance().redisLock(key, value, seconds*1000);
				//Thread currentThread = Thread.currentThread();
				
				
				if (acquireLock) {
					LOG.debug("线程 获取锁成功,!!!!!!!!!!!!!!!!  key:"+key);
					//System.out.println("线程" + currentThread.getId() + "获取锁成功,!!!!!!!!!!!!!!!!  key:"+key);
					return acquireLock;
				} else {
					LOG.debug("线程获取锁失败");
					//System.out.println("线程" + currentThread.getId() + "获取锁失败");
					count++;
					Thread.sleep(100);
					LOG.debug("the " + count + " times try to acquire lock for " + key + "......");
					//System.out.println("the " + count + " times try to acquire lock for " + key + "......");
					continue;
				}
				
				//Thread.sleep(500);
				/*boolean releaseLock = JodisUtils.getInstance().releaseLock(key, uuid);
				
				if (releaseLock) {
					LOG.debug("线程释放锁-------锁成功,!!!!!!!!!!!!!!!!  key:"+key);
					//System.out.println("线程" + currentThread.getId() + "释放锁-------锁成功,!!!!!!!!!!!!!!!!  key:"+key);
					return releaseLock;
				}*/
			} catch (Exception e2) {
				LOG.error(e2.getMessage(), e2);
				return false;
			}
		}
	} 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 使用

public static void main(String[] args) {
	
	
	for (int i = 0; i < 30; i++) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				String key = "testLock";
				String uuid = System.currentTimeMillis()+UUID.randomUUID().toString();
				JodisUtils instance = JodisUtils.getInstance();
				boolean acquireLock = instance.acquireLock(key,uuid,10);
				
				if (acquireLock) {
					//释放锁
					instance.releaseLock(key, uuid);
					System.out.println("线程 释放锁-------锁成功,!!!!!!!!!!!!!!!!  key:"+key);
				}
			}
		}).start();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值