電子商務平臺源碼請加企鵝求求:三五三六二四七二五九。在springcloud項目開發中redis分佈式鎖使用主要有兩個場景redis
1.訂單重複提交或支付提交等,防止刷單spring
2.對某個業務進行鎖定,例如:當用戶同一時間,進行對帳戶充值和提現操做,那麼這裏須要根據用戶ID對帳戶進行鎖定,只有一個完成了才能夠進行第二個。 開發實現方式緩存
1.pom.xml中引入jar包,最好引入到基礎模塊中,其餘模塊通用bash
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
複製代碼
建立redis操做類RedisGlobalLock(自定義)分佈式
redis提供RedisTemplate方法spring-boot
redis提供三個方法:ui
(1)lock 獲取鎖並鎖定 本方法是當即獲取鎖狀態,若是獲取成功並鎖定,若是獲取失敗spa
(2)tryLock 嘗試獲取鎖並鎖定 本方式是在指定時間嘗試獲取鎖code
(3)unlock 釋放鎖 當業務處理完畢必須釋放鎖xml
重點:
lock和tryLock區別:lock是實時獲取,tryLock是嘗試在一段時間內一直在獲取
@Service
public class RedisGlobalLock {
private static Log log = LogFactory.getLog(RedisGlobalLock.class);
private static final String TYPE_NAME = RedisGlobalLock.class.getTypeName();
/** 默認30ms嘗試一次 */
private final static long LOCK_TRY_INTERVAL = 30L;
/** 默認嘗試20s */
private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;
/** 單個業務持有鎖的時間30s,防止死鎖 */
private final static long LOCK_EXPIRE = 30 * 1000L;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 獲取鎖
* @param key 鎖Key
* @return 是否獲取鎖
*/
public boolean lock(String key) {
return getLock(key, 0, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
}
/**
* 獲取鎖
* @param key 鎖Key
* @param expire 有效期
* @param expireUnit 有效期時間單位
* @return 是否獲取鎖
*/
public boolean lock(String key, long expire, TimeUnit expireUnit) {
return getLock(key, 0, expire, expireUnit);
}
/**
* 嘗試獲取鎖
* @param key 鎖Key
* @return 是否獲取鎖
*/
public boolean tryLock(String key) {
return tryLock(key, LOCK_TRY_TIMEOUT, TimeUnit.MILLISECONDS);
}
/**
* 嘗試獲取鎖
* @param key 鎖Key
* @param timeout 等待超時時間
* @param unit 等待超時時間單位
* @return 是否獲取鎖
*/
public boolean tryLock(String key, long timeout, TimeUnit unit) {
// 超時時間轉成毫秒
timeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
return getLock(key,timeout, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
}
/**
* 嘗試獲取鎖
* @param key 鎖Key
* @param timeout 等待超時時間
* @param timeoutUnit 等待超時時間單位
* @param expire 有效期
* @param expireUnit 有效期時間單位
* @return
*/
public boolean tryLock(String key, long timeout, TimeUnit timeoutUnit, long expire, TimeUnit expireUnit) {
// 超時時間轉成毫秒
timeout = TimeUnit.MILLISECONDS.convert(timeout, timeoutUnit);
return getLock(key,timeout, expire, expireUnit);
}
/**
* 釋放鎖
* @param key 鎖Key
*/
public void unlock(String key) {
key = getPrefix(TYPE_NAME) + key;
Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key);
if(null != oldExpireTime && oldExpireTime >= System.currentTimeMillis()) {
// 大於過時時間,則刪除key
redisTemplate.delete(key);
}
}
/**
* 獲取鎖
* @param key 鎖鍵值
* @param timeout 超時時間
* @param time 全局鎖生命週期
* @param unit 時間單位
* @return 是否獲取到鎖
*/
private boolean getLock(String key, long timeout, long time, TimeUnit unit) {
key = getPrefix(TYPE_NAME) + key;
try {
long startTimeMillis = System.currentTimeMillis();
do {
long newValue = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit);
Boolean isOk = redisTemplate.opsForValue().setIfAbsent(key, newValue);
if(isOk) {
// 得到鎖
redisTemplate.expire(key, time, unit);
return true;
}
// 獲取過時時間
Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key);
if(null == oldExpireTime) {
oldExpireTime = 0L;
}
if(oldExpireTime >= System.currentTimeMillis()) {
// 不小於系統時間而且過了超時時間,則不獲取鎖
if((System.currentTimeMillis() - startTimeMillis) > timeout) {
return false;
}
// 休眠
Thread.sleep(LOCK_TRY_INTERVAL);
}
// 新的過時時間
long newExpireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit);
Long currentExpireTime = (Long) redisTemplate.opsForValue().getAndSet(key, newExpireTime);
if(null == currentExpireTime) {
currentExpireTime = 0L;
}
if(currentExpireTime.equals(oldExpireTime)) {
// 獲取到鎖
redisTemplate.expire(key, time, unit);
return true;
}
} while (true);
} catch (Exception e) {
return false;
}
}
/**
* 獲取緩存標識前綴
* @param typeName 類名
* @return 前綴
*/
protected final String getPrefix(String typeName) {
return typeName;
}
}
複製代碼
在業務邏輯層引入redis操做類
@Resource
private RedisGlobalLock redisGlobalLock;
// 一、獲取分佈式鎖防止重複調用 ======================
String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId;
if(redisGlobalLock.lock(key)) {
try{
System.out.println("--處理業務---");
}catch (Exception e){
throw e;
}finally {
// 四、釋放分佈式鎖 ======================
redisGlobalLock.unlock(key);
}
}else{
// 若是沒有獲取鎖
Ensure.that(true).isTrue("17000706");
}
複製代碼
全部鎖業務必須釋放鎖,防止死鎖