quartz集群增強版🎉
quartz集群增強版??
開源地址: https://github.com/funnyzpc/quartz
這是除了mee_admin之外,投入時間精力最多的一次開源了,quartz集群增強版前前后后花費了我四月有余的時間精力開發而來,實屬不易~
先簡要的說下:quartz集群增強版由quartz的 2.3.2 版本改造,對原有功能進行了部分縮減, 同時也對現有的痛點做了大量的增強,這些增強包括但不限于如下:
1. 修改任務參數序列化及存儲
將默認參數存儲方式由blob大字段方式修改為定長字符串方式,保證后管修改即所得,同時內部使用 org.json 庫對json字符串序列化及反序列化,使用起來更便捷
同時也將傳參配置由原版的 K/V(對象序列化) 改為(Map)對象(eg:{"aa":1})或(Array)列表(eg:[11,true,{"bb":"her"}]) 的方式,使用時視json結構,可使用
context.getJobDataMap() 或 context.getJobDataList() 直接獲取配置的json參數,更簡單快捷;順帶一提:也可通過 context.getJobData() 拿出原始存儲的json字符串??
2. 分離了執行端及配置管理端
這是很大的改變,如果是執行端使用,只需添加依賴 quartz-core,如果是后管配置則只需要連接執行庫(數據庫)后使用依賴 quartz-client 來配置,管理集群或分布式的執行端也只需要
使用此來配置即可(前提是連接同一個庫表), 這個改變在原版的quartz中是不可想象的,原版的quartz執行端與配置端耦合這種不太美妙的方式著實令人頭疼與不解??
3. 使用樂觀鎖
這同樣也是重大的變化,原版采用lock表行級悲觀鎖,每次變更都要競爭同一個 STATE_ACCESS 或 TRIGGER_ACCESS ,同時每次執行加鎖釋放鎖的過程還需配合線程本地變量(ThreadLocal)來使用,看起來有些沒必要而十分的笨重的同時系統開銷也比較大
quartz集權增強版不再使用 lock 表悲觀鎖,而是使用 UPDATE ... WHERE ... 方式(樂觀鎖),雖同樣存在一定的查詢開銷,但寫db的開銷大大地減少了。
同時,需要說明的是使用樂觀鎖似乎還是有些不夠,可目前并沒有測試出bug(也許是測試不夠嚴格吧...),如果有好的idea 懇請告知哈~ ??
4. 簡化了表結構以及數量
由于底層去掉了trigger及 memory 單機等相關邏輯,故表結構及邏輯也做了相應的簡化,由原版的11張表簡化為4張表,對,沒錯~,就是四張表,四張表就ok??
表的整體設計主要參考了 mee_timed 這個同樣由我寫的定時任務組件,目前 這四張表為:
- QRTZ_APP 應用表: 用于定義應用,尤其是對于管理分布式應用十分有用,同時執行端啟動時會自動新增或更新 無需手動添加數據
- QRTZ_NODE 節點表:用于定義應用下的執行節點,這是對于集群應用十分有用,比如你需要增量發布時可通過啟停節點以增量更新執行端,同樣表數據也是自動新增或更新,十分方便
- QRTZ_JOB 任務配置表: 配置任務的基本信息(不包含執行事件項,一個job任務對應多個執行配置/時間項),此表主要定義任務的執行類以及關聯的應用信息
- QRTZ_EXECUTE 執行配置表: 執行配置必須關聯一個任務配置(job),執行配置也有獨立的狀態可供后管操作
5. 去掉了group(組)
這是個不太有用的概念,組在絕大數使用quartz的開發者來說十分困惑(至少我經歷的項目大多是這樣),group的使用如有不慎會跟預期存在差異,因為有 group 這一層的存在,配置任務也略顯有些臃腫。。。????
去掉了 group 的同時也去掉了 TriggerKey 及相關的邏輯,這樣就基本淡化了 group 的概念及使用,好處不言而喻。
6. 兼容原版quartz的配置項及集成方式
對于常用的springboot框架,集成方式與原版的quartz的方式并無太大區別,主要區別是只需導入 quartz集群增強版 提供的表即可,starter(autoconfigure)中無用的配置類及方法都做了兼容
還有就是原有的context稍有變化,主要是去掉了TriggerKey以及增強json傳值帶來的變更,基本使用就無任何區別~ ????
7. 缺火/熄火(MisfireHandler)及集群(ClusterManager)處理
首先,這個變更也蠻大的。。。
先說下缺火是什么,由于quartz內所有執行時間點都是通過對應類型的 Trigger(eg: CronTriggerImpl、 SimpleTriggerImpl) 計算出來的,一旦出現不可預知的錯誤以及停機,則執行時間點無法向前推進,如果補償或恢復機制,則 fireTrigger 的任務掃描無法掃到造成任務無法繼續執行,這就是缺火(Misfire),
解釋的并不好,請大神給斧正哈??
因為原版的quartz在這兩塊存在并發(集群) 以及全局悲觀鎖的存在,一旦觸發 Misfire 則可能導致任務存在不可知的問題,同時 MisfireHandler、ClusterManager 為兩個獨立不同的線程任務(輪詢),但使用的鎖是同一把,邏輯處理就存在時效性問題(延遲)
所以對于本 quartz集群增強版 就不同了????, 現在我將這兩招合一, 使用 ClusterMisfireHandler 來處理 Misfire 任務以及節點check及清理任務,同樣是使用數據庫鎖,但是在 ClusterMisfireHandler 內部做了并發優化,以保證同一時間一個應用下只有一個節點執行,這點而很重要!
8. 其他
- 簡化了線程池的管理同時也兼容原有的
SimpleThreadPool - 由于傳參的變化也無需對存儲參數的大字段的操作做各個廠商的數據庫的兼容
架構設計
quartz-client+后管集成效果
目前可能存在及已知的問題
- 不管是 ClusterMisfireHandler 維護集群及缺火,還是 QuartzSchedulerThread 掃任務 都是按頻度來的,其中 QuartzSchedulerThread 是5s一次循環 ,ClusterMisfireHandler 則是15s一次循環,這樣問題就來了
- 添加任務時,若您是10點整添加的任務則任務最快也得10點過5s后才可執行,這是目前 任務掃描的頻度及當前架構而為之的
- QuartzSchedulerThread 在掃描批次任務后不斷循環以到執行時間點時,若關閉節點及應用也只有等當前次執行完成后才會停止任務
- QuartzSchedulerThread 的鎖的處理(集群環境下如何保證一個任務只被一個節點執行)可能存在缺陷,但目前排查不出問題,這需要使用者花時間測試以排查根本原有
- 去掉 group 對于 group 的使用者來說這是不好的消息,這算一個吧??
- 目前僅支持 postgresql、mysql、oracle 這三個廠商的數據庫支持,并經過測試通過,這是使用限制
- client sdk (
quartz-client) 雖已經過測試通過,可能存在未知bug及設計缺陷





浙公網安備 33010602011771號