基于Mongodb分布式鎖解決定時(shí)任務(wù)并發(fā)執(zhí)行問題
前言
我們?nèi)粘i_發(fā)過程,會(huì)有一些定時(shí)任務(wù)的代碼來統(tǒng)計(jì)一些系統(tǒng)運(yùn)行數(shù)據(jù),但是我們應(yīng)用有需要部署多個(gè)實(shí)例,傳統(tǒng)的通過配置文件來控制定時(shí)任務(wù)是否啟動(dòng)又太過繁瑣,而且還經(jīng)常出錯(cuò),導(dǎo)致一些異常數(shù)據(jù)的產(chǎn)生
網(wǎng)上有很多分布式鎖的實(shí)現(xiàn)方案,基于redis、zk、等有很多,但是我的就是一個(gè)用了mysql和mongo的小應(yīng)用,不準(zhǔn)備引入其他三方中間件來解決這個(gè)問題,擼一個(gè)簡(jiǎn)單的分布式鎖來解決定時(shí)任務(wù)并發(fā)執(zhí)行的問題,加鎖操作的原子性和防死鎖也都要支持,這里我使用mongodb寫了AllInOne的工具類
All in one Code
先上代碼
@Component
@Slf4j
public class MongoDBLock {
private static final int DEFAULT_LOCK_TIMEOUT = 30;//鎖的默認(rèn)超時(shí)時(shí)間,單位秒
private MongoTemplate mongoTemplate;
private int lockTimeout;
public MongoDBLock(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
this.lockTimeout = DEFAULT_LOCK_TIMEOUT;
}
/**
* 嘗試獲取分布式鎖
*
* @param lockKey 鎖的key
* @return true:獲取鎖成功,false:獲取鎖失敗
*/
private boolean acquireLock(String lockKey) {
LockDocument document = new LockDocument();
document.setId(lockKey);
document.setExpireAt(Instant.ofEpochMilli(Instant.now().toEpochMilli() + lockTimeout * 1000));
try {
mongoTemplate.insert(document);
return true;
} catch (Exception e) {
}
return false;
}
/**
* 釋放分布式鎖
*
* @param lockKey 鎖的key
*/
private void releaseLock(String lockKey) {
Query query = new Query(Criteria.where("key").is(lockKey));
mongoTemplate.remove(query, LockDocument.class);
log.info("程序執(zhí)行成功,釋放分布式鎖,lockKey:{}",lockKey);
}
/**
* 分布式鎖入口方法,參數(shù)lockName為鎖的名稱,lockKey為需要加鎖的key,執(zhí)行完成后自動(dòng)釋放鎖
*
* @param lockKey
* @param task
* @param <T>
* @throws Exception
*/
public <T> void executeWithLock(String lockKey, ITask<T> task) throws Exception {
boolean locked = acquireLock(lockKey);
if (locked) {
log.info("獲取分布式鎖成功,lockKey:{}",lockKey);
try {
task.execute();
} finally {
releaseLock(lockKey);
}
} else {
log.warn("獲取分布式鎖失敗,lockKey:{}", lockKey);
throw new AppException("獲取分布式鎖失敗!");
}
}
@Data
@Document(collection = "lock_collection")
static class LockDocument {
@Id
private String id;
@Indexed(expireAfterSeconds = DEFAULT_LOCK_TIMEOUT)
private Instant expireAt;
}
@FunctionalInterface
public interface ITask<T> {
T execute() throws Exception;
}
}
調(diào)用示例
@Resource
MongoDBLock mongoDBLock;
mongoDBLock.executeWithLock("key", () -> {
// do some thing
return null;
});原理
- 使用key作為主鍵,利用mongodb的insert原子性保障LockDocument不會(huì)重復(fù)插入
- LockDocument中expireAt字段利用的mongodb索引過期機(jī)制,解決死鎖問題,這里設(shè)置超時(shí)時(shí)間是30秒,并在執(zhí)行完成之后會(huì)主動(dòng)釋放鎖
到此這篇關(guān)于基于Mongodb分布式鎖簡(jiǎn)單實(shí)現(xiàn),解決定時(shí)任務(wù)并發(fā)執(zhí)行問題的文章就介紹到這了,更多相關(guān)Mongodb分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MongoDB自動(dòng)刪除過期數(shù)據(jù)的方法(TTL索引)
這篇文章主要給大家介紹了關(guān)于MongoDB自動(dòng)刪除過期數(shù)據(jù)(TTL索引)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11
通用MapReduce程序復(fù)制HBase表數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了通用MapReduce程序復(fù)制HBase表數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
MongoDB對(duì)數(shù)組進(jìn)行增刪改查操作
與關(guān)系型數(shù)據(jù)庫相比,MongoDB支持?jǐn)?shù)組,將數(shù)組存儲(chǔ)到文檔之中,下面這篇文章主要給大家介紹了關(guān)于MongoDB對(duì)數(shù)組進(jìn)行增刪改查操作的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05

