Spring-Quartz事務問題的排查總結

環境

  • Quartz集羣模式,使用數據庫持久化任務
  • Spring接管Quartz,即Quartz依賴於Spring運行

問題

在一套集羣應用(4個實例節點)啓動的時候,應用調用了QuartzScheduler#scheduleJob初始化定時任務,會偶然觸發JobPersistenceException: could not store trigger : unique constraint violdated.即多個節點同時scheduleJob的時候了違反約束條件.java

問題代碼大概以下:數據庫

public class MyQuartzConfig {
    @Autowired
    private Scheduler scheduler;
    
    public void init() {
        scheduler.scheduleJob(job, trigger);
    }
}
複製代碼

咱們都知道,Quartz集羣模式是借用了數據庫來實現競爭鎖的,若是某個節點獲取鎖失敗,會等待一段時間後再次嘗試,直到超太重試次數拋出異常.上述出現違反約束條件確定是由於鎖沒有生效,換句話說事務沒有生效spa

緣由

Quartz JobStore

Quartz提供了兩個持久化任務的實現類,分別是JobStoreCMTJobStoreTXcode

  • JobStoreTX很簡單,裏面只有一個數據源,持久化的事務提交和回滾都由它來控制
  • JobStoreCMT有兩個數據源,一個數據源TxDataSource,即包含事務的數據源,另外一個NonTxDataSource,不包含事務的數據源

JobStoreCMT的兩個數據源怎麼理解呢?根據官方註釋能夠看到,TxDataSource事務受容器管理,可應用於JTA等XA場合. 而NonTxDataSource事務不受容器管理,是Quartz自行管理.cdn

小結:CMT的NonTxDataSource便是JobStoreTX的數據源,Quartz獲取connection後會設置autoCommit=false,由Quartz自行控制事務.blog

而JobStoreCMT的TxDataSource是額外添加的,爲的是讓用戶可以控制這些事務,給用戶更大的靈活性.事務

JobStore在不一樣的場景下都會用到這兩種數據源,分別是executeInLockexecuteInNonManagedTXLock文檔

  • executeInLock,與TxDataSource對應,基本上是提供給用戶調用的API
  • executeInNonManagedTXLock,與NonTxDataSource對應,都是Quartz內部的API,與用戶無關

Spring-Quartz

當Spring接管Quartz後,狀況會發生一些變化. 若是Spring提供的SchedulerFactoryBean設置了數據源,就會用LocalDataSourceJobStore來替代Quartz的JobStore類. it

LocalDataSourceJobStore實際上是一個JobStoreCMT,它的TxDataSource受Spring事務管理,NonTxDataSource則不會. 同時他會將從TxDataSource獲取的connection.autoCommit=ture

真正緣由

看到這裏應該能猜到緣由了,在咱們的例子裏,調用scheduleJob方法使用的數據源是TxDataSource,而持久化類LocalDataSourceJobStore默認autoCommit=ture,在這種狀況下其實咱們調用時是不存在事務的,天然就會出現文中的問題.io

解決問題

沒有事務的話給它加上事務就行了,因爲咱們用到Spring-Quartz,加上Spring的事務就能解決問題

public class MyQuartzConfig {
    @Autowired
    private Scheduler scheduler;
    @Transactional  // 加上事務註解便可解決問題
    public void init() {
        scheduler.scheduleJob(job, trigger);
    }
}
複製代碼

總結

Quartz提供JobStoreCMT是爲了讓用戶調用Quartz的時候有事務控制(例如但願多個Quartz實例同時成功或同時失敗).

我也不知道之後怎麼避免這類問題,Spring提供的文檔好像沒有說明事務的問題,難道真的只能好好看註釋了?

相關文章
相關標籤/搜索