2016-05-11 30 views
0

Related: Quartz Clustering - triggers duplicated when the server starts在集羣環境中

創建石英觸發器我使用Quartz調度在基於Java的集羣環境管理計劃的作業。羣集中有幾個節點在任何時候都運行Quartz,由所有節點連接到的postgresql數據庫中的數據存儲支持。

當一個實例初始化,它試圖創建或更新工作,並通過執行該代碼在石英數據存儲觸發:

private void createOrUpdateJob(JobKey jobKey, Class<? extends org.quartz.Job> clazz, Trigger trigger) throws SchedulerException { 
    JobBuilder jobBuilder = JobBuilder.newJob(clazz).withIdentity(jobKey); 
    if (!scheduler.checkExists(jobKey)) { 
     // if the job doesn't already exist, we can create it, along with its trigger. this prevents us 
     // from creating multiple instances of the same job when running in a clustered environment 
     scheduler.scheduleJob(jobBuilder.build(), trigger); 
     log.error("SCHEDULED JOB WITH KEY " + jobKey.toString()); 
    } else { 
     // if the job has exactly one trigger, we can just reschedule it, which allows us to update the schedule for 
     // that trigger. 
     List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); 
     if (triggers.size() == 1) { 
      scheduler.rescheduleJob(triggers.get(0).getKey(), trigger); 
      return; 
     } 

     // if for some reason the job has multiple triggers, it's easiest to just delete and re-create the job, 
     // since we want to enforce a one-to-one relationship between jobs and triggers 
     scheduler.deleteJob(jobKey); 
     scheduler.scheduleJob(jobBuilder.build(), trigger); 
    } 
} 

這種方法解決了許多問題:

  1. 如果環境配置不正確(即作業/觸發器不存在),那麼它們將通過第一個實例創建,它將啓動
  2. 如果作業已經存在,但我想修改其時間表(更改jo b每7分鐘運行一次,現在每5分鐘運行一次),我可以爲它定義一個新的觸發器,重新部署將重新安排數據庫中的觸發器
  3. 將創建一個作業的實例,因爲我們始終使用由作業本身定義的指定JobKey來引用作業。這意味着作業(及其關聯的觸發器)只創建一次,而不管集羣中有多少個節點,或者部署多少次。

這一切都很好,但我關心兩個實例在同一時間啓動時潛在的競爭條件。因爲這個代碼沒有集羣中所有節點都會尊重的全局鎖定,所以如果兩個實例同時聯機,那麼最終可能會出現重複的作業或觸發器,這會破壞此代碼的重點。

在羣集環境中自動定義Quartz作業和觸發器是否有最佳做法?或者我需要訴諸設置自己的鎖?

回答

1

我不確定是否有更好的方法在Quartz中做到這一點。但是,如果您已經在使用Redis或Memcache,我會建議讓所有實例針對衆所周知的密鑰執行atomic increment。如果粘貼的代碼應該運行每個集羣每小時只有一個工作,你可以做到以下幾點:

long timestamp = System.currentTimeMillis()/1000/60/60; 
String key = String.format("%s_%d", jobId, timestamp); 

// this will only be true for one instance in the cluster per (job, timestamp) tuple 
bool shouldExecute = redis.incr(key) == 1 

if (shouldExecute) { 
    // run the mutually exclusive code 
} 

時間戳給你一個移動窗口內的作業都爭相來執行這項工作。

+0

這並不是我正在尋找的東西,因爲它不會阻止Quartz爲同一個作業創建多個觸發器,但它確保只有其中一個觸發器會在給定的窗口,所以我認爲它確實解決了問題,如果以一種迂迴的方式。 – MusikPolice

0

我有(幾乎)同樣的問題:如何在集羣環境中爲每個軟件版本創建一次觸發器和作業。我通過在啓動過程中將其中一個羣集節點指定爲主節點並讓它重新創建Quartz作業來解決問題。主節點是首先成功地將正在運行的軟件的git修訂版號插入數據庫的節點。其他節點使用由主節點創建的Quartz配置。這裏有完整的解決方案:https://github.com/perttuta/quartz