網站首頁 編程語言 正文
在開發的過程中,項目中使用定時器已經不是一個新鮮的事情了,但是如果你的項目后期部署到集群環境下,如果不做處理,就會出現意想不到的問題,原因:由于我們項目同時部署在多臺集群機器上,因此到達指定的定時時間時,多臺機器上的定時器可能會同時啟動,造成重復數據或者程序異常等問題,下面我提供幾種解決方案
一、固定執行定時任務的機器
方法:在多臺機器中選擇一臺執行定時任務,每次執行的時候回判斷當前機器和指定的機器是否一致或者啟動時就指定好執行機器
優缺點:這種方法是可以有效避免多次執行的情況,,但是最明顯的缺點就是單點故障問題,如果你指定的機器出現了宕機,,任務就不會執行了,業務邏輯就會奔潰。
二、在數據庫建立多張表,從定時任務表中獲取定時方法
方法:由于MySQL存在表鎖和行鎖(MyISAM引擎只支持表鎖,而InnoDB支持行鎖和表鎖兩種),每次執行定時任務的時候從數據庫表中讀取記錄,只有讀取到的記錄標識當前任務狀態為未執行時,當前機器才會去觸發任務,并且更新數據庫狀態(先更新,再執行),由于存在表鎖和行鎖,因此同一時刻只能有一個事務操作,可以保證只執行一次
優缺點:這種方法是一種比較合適的方式,但是需要有多張表,并且已經做了定時器邏輯上會有較大的改動
三、借助Redis的過期機制和分布式鎖
方法:為你的定時器在Redis中定義一個鍵值對,可以用項目名稱和服務器ip,執行任務前先從Redis中讀取鍵,若沒有值代表任務未被執行,同樣的該臺機器先更新redis,再觸發定時任務。由于Redis存在過期機制,因此可以設置過期時間保證下次判斷正常
優缺點:該方法個人比較推薦,簡單,對業務邏輯的改變也會少很多,只需要在原來的定時器上加上簡單判斷即可
四、Quartz的集群應用方式
方法:如果你的項目使用的是Spring自帶有Task定時任務機制,quartz框架本身就是支持集群環境,可以搭建集群環境下的定時器,也能解決上述問題 不過需要配置11張數據庫表
優缺點:該解決方案最大的問題是需要配置11張左右的數據庫表,工作量非常大
以下是 ( 借助Redis的過期機制和分布式鎖 ) 的實現方案
1、需要添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、具體代碼
@Autowired
private RedisTemplate redisTemplate;
@Scheduled(cron = "0 */1 * * * ?")
public void generateData() {
/** 定時任務的名稱作為key **/
String key = "generateData";
/** 設置隨機key **/
String value = UUID.randomUUID().toString().replace("-", "");
//如果鍵不存在則新增,存在則不改變已經有的值。(備注:失效時間要大于多臺服務器之間的時間差,如果多臺服務器時間差大于超時時間,定時任務可能會執行多次)
Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, value, 20, TimeUnit.SECONDS);
if (flag != null && flag) {
log.info("{} 鎖定成功,開始處理業務", key);
try {
/** 模擬處理業務邏輯,15秒 **/
Thread.sleep(1000 * 15);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 業務邏輯處理完畢,釋放鎖,正常情況下,由于上邊 setIfAbsent 已經設置過期時間了,
* 所以在規定時間內,Redis 會自動刪除過期的 key,但是這個刪除由于不確實是什么刪除策略,
* 所以最后執行完再刪除一遍比較保險。
*/
String lockValue = (String) redisTemplate.opsForValue().get(key);
/** 只有:值未被釋放(也就是當前未達到過期時間),且是自己加鎖設置的值(不要釋放別人的所),這種情況下才會釋放鎖 **/
if (lockValue != null && lockValue.equals(value)) {
redisTemplate.delete(key);
log.info("{} 解鎖成功,結束處理業務", key);
}
} else {
log.info("{} 獲取鎖失敗", key);
}
}
3、如果定時任務在拿到鎖后執行失敗怎么辦?
解決方法:添加一個手動提交的接口,在前端頁面可以手動提交
原文鏈接:https://blog.csdn.net/qq_42846807/article/details/125771941
- 上一篇:淺談常見的加密算法
- 下一篇:uniCloud云開發獲取小程序用戶openid
相關推薦
- 2022-07-12 微信小程序(條件渲染和列表渲染)
- 2022-07-15 教你docker方式部署nacos_docker
- 2023-01-11 Pytorch如何把Tensor轉化成圖像可視化_python
- 2022-04-17 Python?同級目錄(兄弟目錄)調用方式_python
- 2022-09-21 淺析C++函數模板和類模板_C 語言
- 2022-08-19 關于?React?中?useEffect?使用問題淺談_React
- 2021-09-17 TortoiseGit的安裝與配置教程_相關技巧
- 2022-07-16 【Maven】多模塊構建項目的維護
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支