一、Redis分佈式鎖實現思路
Redis實現分佈式鎖基於SetNx命令,因爲在redis中key是保證是唯一的。所以當多個線程同時創建setNx時,只要誰能夠創建成功誰就能獲取到鎖。
Set命令:每次set時,可以修改原來舊值;
SetNx命令:每次SetNx檢查key是否已經存在,如果已經存在的話就不會執行任何操作,返回0;反之,新增該key。
獲取鎖的時候:當多個線程同時創建SetNx key,只要誰能夠創建成功誰就能夠獲取到鎖。
釋放鎖:可以對該key設置一個有效期可以避免死鎖的現象。
二、Zookeeper實現分佈式鎖思路
Zookeeper實現分佈式鎖核心採用臨時節點+事件通知,因爲Zookeeper節點路徑是保證全局唯一的,當多個線程同時創建該臨時節點,只要誰能夠創建成功誰就能獲取到鎖。
獲取鎖:當多個線程同時創建該臨時節點,只要誰能夠創建成功誰就能夠獲取到鎖。
釋放鎖:關閉當前Session連接,自動的刪除當前的zk節點路徑,其他線程重新進入到獲取鎖階段。
三、分佈式鎖應用場景
分佈式任務調度平臺保證任務的冪等性。
分佈式全局id的生成
四、Redis分佈式鎖核心代碼
public class RedisUtil {
//protected static Logger logger = Logger.getLogger(RedisUtil.class);
private static String IP = "192.168.75.128";
//Redis的端口號
private static int PORT = 6379;
//可用連接實例的最大數目,默認值爲8;
//如果賦值爲-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態爲exhausted(耗盡)。
private static int MAX_ACTIVE = 100;
//控制一個pool最多有多少個狀態爲idle(空閒的)的jedis實例,默認值也是8。
private static int MAX_IDLE = 20;
//等待可用連接的最大時間,單位毫秒,默認值爲-1,表示永不超時。如果超過等待時間,則直接拋出JedisConnectionException;
private static int MAX_WAIT = 3000;
private static int TIMEOUT = 3000;
//在borrow一個jedis實例時,是否提前進行validate操作;如果爲true,則得到的jedis實例均是可用的;
private static boolean TEST_ON_BORROW = true;
//在return給pool時,是否提前進行validate操作;
private static boolean TEST_ON_RETURN = true;
private static JedisPool jedisPool = null;
/**
* redis過期時間,以秒爲單位
*/
public final static int EXRP_HOUR = 60 * 60; //一小時
public final static int EXRP_DAY = 60 * 60 * 24; //一天
public final static int EXRP_MONTH = 60 * 60 * 24 * 30; //一個月
/**
* 初始化Redis連接池
*/
private static void initialPool() {
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_ACTIVE);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(MAX_WAIT);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, IP, PORT, TIMEOUT, "123456");
} catch (Exception e) {
//logger.error("First create JedisPool error : "+e);
e.getMessage();
}
}
/**
* 在多線程環境同步初始化
*/
private static synchronized void poolInit() {
if (jedisPool == null) {
initialPool();
}
}
/**
* 同步獲取Jedis實例
*
* @return Jedis
*/
public synchronized static Jedis getJedis() {
if (jedisPool == null) {
poolInit();
}
Jedis jedis = null;
try {
if (jedisPool != null) {
jedis = jedisPool.getResource();
}
} catch (Exception e) {
e.getMessage();
// logger.error("Get jedis error : "+e);
}
return jedis;
}
/**
* 釋放jedis資源
*
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null && jedisPool != null) {
jedisPool.returnResource(jedis);
}
}
public static Long sadd(String key, String... members) {
Jedis jedis = null;
Long res = null;
try {
jedis = getJedis();
res = jedis.sadd(key, members);
} catch (Exception e) {
//logger.error("sadd error : "+e);
e.getMessage();
}
return res;
}
}
public class RedisLock {
private static final int setnxSuccss = 1;
/**
* 獲取鎖
*
* @param lockKey 定義鎖的key
* @param notLockTimeOut 沒有獲取鎖的超時時間
* @param lockTimeOut 使用鎖的超時時間
* @return
*/
public String getLock(String lockKey, int notLockTimeOut, int lockTimeOut) {
// 獲取Redis連接
Jedis jedis = RedisUtil.getJedis();
// 定義沒有獲取鎖的超時時間
Long endTimeOut = System.currentTimeMillis() + notLockTimeOut;
while (System.currentTimeMillis() < endTimeOut) {
String lockValue = UUID.randomUUID().toString();
// 如果在多線程情況下誰能夠setnx 成功返回0 誰就獲取到鎖
if (jedis.setnx(lockKey, lockValue) == setnxSuccss) {
jedis.expire(lockKey, lockTimeOut / 1000);
return lockValue;
}
// 否則情況下 在超時時間內繼續循環
}
try {
if (jedis != null) {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 釋放鎖 其實就是將該key刪除
*
* @return
*/
public Boolean unLock(String lockKey, String lockValue) {
Jedis jedis = RedisUtil.getJedis();
// 確定是對應的鎖 ,才刪除
if (lockValue.equals(jedis.get(lockKey))) {
return jedis.del(lockKey) > 0 ? true : false;
}
return false;
}
}
public class TestService {
private RedisLock redisLock = new RedisLock();
private String lockKey = "redis_lock";
public void service() {
// 1.獲取鎖
String lockValue = redisLock.getLock(lockKey, 5000, 5000);
if (StringUtils.isEmpty(lockValue)) {
System.out.println(Thread.currentThread().getName() + ",獲取鎖失敗!");
return;
}
// 2.獲取鎖成功執行業務邏輯
System.out.println(Thread.currentThread().getName() + ",獲取成功,lockValue:" + lockValue);
// 3.釋放lock鎖
redisLock.unLock(lockKey, lockValue);
}
}