- 概述
分布式锁,如果你有多个机器在访问同一个共享资源,
那么这个时候,如果你需要加个锁,让多个分布式的机器在访问共享资源的时候串行起来
那么这个时候,那个锁,多个不同机器上的服务共享的锁,就是分布式锁
分布式锁当然有很多种不同的实现方案,redis分布式锁,zookeeper分布式锁
- 对比
数据库锁:
优点:直接使用数据库,使用简单。
缺点:分布式系统大多数瓶颈都在数据库,使用数据库锁会增加数据库负担。
缓存锁:
优点:性能高,实现起来较为方便,在允许偶发的锁失效情况,不影响系统正常使用,建议采用缓存锁。
缺点:通过锁超时机制不是十分可靠,当线程获得锁后,处理时间过长导致锁超时,就失效了锁的作用。
zookeeper锁:
优点:不依靠超时时间释放锁;可靠性高;系统要求高可靠性时,建议采用zookeeper锁。
缺点:性能比不上缓存锁,因为要频繁的创建节点删除节点。
-
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();
- 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();
}
}