java基于mongodb實現(xiàn)分布式鎖的示例代碼
原理
通過線程安全findAndModify 實現(xiàn)鎖
實現(xiàn)
定義鎖存儲對象:
/**
* mongodb 分布式鎖
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "distributed-lock-doc")
public class LockDocument {
@Id
private String id;
private long expireAt;
private String token;
}
定義Lock API:
public interface LockService {
String acquire(String key, long expiration);
boolean release(String key, String token);
boolean refresh(String key, String token, long expiration);
}
獲取鎖:
@Override
public String acquire(String key, long expiration) {
Query query = Query.query(Criteria.where("_id").is(key));
String token = this.generateToken();
Update update = new Update()
.setOnInsert("_id", key)
.setOnInsert("expireAt", System.currentTimeMillis() + expiration)
.setOnInsert("token", token);
FindAndModifyOptions options = new FindAndModifyOptions().upsert(true)
.returnNew(true);
LockDocument doc = mongoTemplate.findAndModify(query, update, options,
LockDocument.class);
boolean locked = doc.getToken() != null && doc.getToken().equals(token);
// 如果已過期
if (!locked && doc.getExpireAt() < System.currentTimeMillis()) {
DeleteResult deleted = this.mongoTemplate.remove(
Query.query(Criteria.where("_id").is(key)
.and("token").is(doc.getToken())
.and("expireAt").is(doc.getExpireAt())),
LockDocument.class);
if (deleted.getDeletedCount() >= 1) {
// 成功釋放鎖, 再次嘗試獲取鎖
return this.acquire(key, expiration);
}
}
log.debug("Tried to acquire lock for key {} with token {} . Locked: {}",
key, token, locked);
return locked ? token : null;
}
原理:
- 先嘗試upsert鎖對象,如果成功且token一致,說明拿到鎖
- 否則加鎖失敗
- 如果未拿到鎖,但是鎖已過期,嘗試刪除鎖
- 如果刪除成功,再次嘗試拿鎖
- 如果失敗,說明鎖可能已經(jīng)續(xù)期了
釋放和續(xù)期鎖:
@Override
public boolean release(String key, String token) {
Query query = Query.query(Criteria.where("_id").is(key)
.and("token").is(token));
DeleteResult deleted = mongoTemplate.remove(query, LockDocument.class);
boolean released = deleted.getDeletedCount() == 1;
if (released) {
log.debug("Remove query successfully affected 1 record for key {} with token {}",
key, token);
} else if (deleted.getDeletedCount() > 0) {
log.error("Unexpected result from release for key {} with token {}, released {}",
key, token, deleted);
} else {
log.error("Remove query did not affect any records for key {} with token {}",
key, token);
}
return released;
}
@Override
public boolean refresh(String key, String token,
long expiration) {
Query query = Query.query(Criteria.where("_id").is(key)
.and("token").is(token));
Update update = Update.update("expireAt",
System.currentTimeMillis() + expiration);
UpdateResult updated =
mongoTemplate.updateFirst(query, update, LockDocument.class);
final boolean refreshed = updated.getModifiedCount() == 1;
if (refreshed) {
log.debug("Refresh query successfully affected 1 record for key {} " +
"with token {}", key, token);
} else if (updated.getModifiedCount() > 0) {
log.error("Unexpected result from refresh for key {} with token {}, " +
"released {}", key, token, updated);
} else {
log.warn("Refresh query did not affect any records for key {} with token {}. " +
"This is possible when refresh interval fires for the final time " +
"after the lock has been released",
key, token);
}
return refreshed;
}
使用
private LockService lockService;
private void tryAcquireLockAndSchedule() {
while (!this.stopSchedule) {
// 嘗試拿鎖
this.token = this.lockService.acquire(SCHEDULER_LOCK, 20000);
if (this.token != null) {
// 拿到鎖
} else {
// 等待LOCK_EXPIRATION, 再次嘗試
Thread.sleep(LOCK_EXPIRATION);
}
}
}
- 先嘗試拿鎖,如果獲取到token,說明拿鎖成功
- 否則可以sleep一段時間后再拿鎖
到此這篇關(guān)于java基于mongodb實現(xiàn)分布式鎖的示例代碼的文章就介紹到這了,更多相關(guān)java mongodb實現(xiàn)分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springmvc圖片上傳及json數(shù)據(jù)轉(zhuǎn)換過程詳解
這篇文章主要介紹了springmvc圖片上傳及json數(shù)據(jù)轉(zhuǎn)換過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-10-10
解決Spring?Boot應(yīng)用打包后文件訪問問題
在Spring Boot項目的開發(fā)過程中,一個常見的挑戰(zhàn)是如何有效地訪問和操作資源文件,本文就來介紹一下解決Spring?Boot應(yīng)用打包后文件訪問問題,感興趣的可以了解一下2024-01-01
Java開發(fā)之普通web項目轉(zhuǎn)為Maven項目的方法
這篇文章主要給大家介紹了關(guān)于Java開發(fā)之普通web項目轉(zhuǎn)為Maven項目的相關(guān)資料,文中通過圖文將轉(zhuǎn)換的方法步驟介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
java通過JFrame做一個登錄系統(tǒng)的界面完整代碼示例
這篇文章主要介紹了java通過JFrame做一個登錄系統(tǒng)的界面完整代碼示例,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12
IDEA運行導(dǎo)入的javaweb項目tomcat正常,但是運行失敗404問題
這篇文章主要介紹了IDEA運行導(dǎo)入的javaweb項目tomcat正常但是運行失敗404問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07

