Spring提供了定時任務的功能,但是在多個例項的叢集中,會出現定時任務重複執行多次的情況。
使用Qutaz框架自帶的分散式定時任務可以很好的解決這個問題,但是講道理功能有些過於強大,對於需求不高,乃至可以一定程度上允許失誤的簡單任務中,價效比比較低。
使用task任務時,可以通過在redis等快取、資料庫中建立鎖來實現避免重複執行任務的功能。
基本思路如下:
- 在redis中設定一個key作為鎖,值為時間戳
- 嘗試用setnx方法直接設定鎖,若成功則直接執行任務。
- 若設定失敗,使用getset方法獲取當前鎖並更新時間戳
- 若獲取的時間戳在有效期內,則不執行任務,否則執行任務
當然這種實現方法是很粗糙的。對於高併發的頻繁定時任務處理很不完美。若執行失敗也會導致任務丟失。
本方案實現的處理場景是間隔比較長的資料處理定時任務。伺服器叢集也只有三個節點,上線執行效果良好。
簡要實現程式碼如下:
private boolean getLock(String key){
String syncKey = "sync_lock_"+key;
long curr = System.currentTimeMillis();
String time = curr+"";
long has = redisTemplate.setnx(syncKey,time);
if(has == 1){
return true;
}else{
String lock = redisTemplate.getSet(syncKey,time);
if(lock == null){
return true;
}else{
long l = NumberUtils.toLong(lock);
//三十秒以內都算有效鎖
return Math.abs(curr-l)<30000;
}
}
}
複製程式碼