1. 程式人生 > >Spring+Quartz 叢集(含提示序列化錯誤)

Spring+Quartz 叢集(含提示序列化錯誤)

這幾天給Spring+Quartz的叢集折騰得死去活來,google了無數頁總算搞定,記下一些要點備以後使用。 

單獨的Quartz叢集在http://unmi.blogjava.net/有Unmi翻譯的Quartz Job Scheduling Framework一書做了詳細說明,在此不再重複。 

Spring+Quartz不叢集的方式google百度也可以搜尋出來一大堆,同樣略過。 

要點1 在Spring中使用Quartz的高階配置 
問題描述
 Quartz叢集僅能使用JDBC JobStore工作,需要在Spring中使用Quartz的高階配置 
解決辦法1.1 通過SchedulerFactoryBean的configLocation屬性指定Quartz配置檔案的位置。 
Xml程式碼  收藏程式碼
  1. <
    bean id="quartzJobFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  2.     <property name="triggers">  
  3.         <list>  
  4.             <ref bean="clusterTesterJobScheduledTask" />  
  5.         </list>  
  6.     </property>  
  7.     <property name="configLocation"
     value="classpath:quartz.properties" />  
  8. </bean>  

解決辦法1.2 通過SchedulerFactoryBean的quartzProperties屬性直接配置。 

要點2 NotSerializableException 
問題描述
 在將Quartz的Job持久化到資料庫的過程中產生NotSerializableException。詳細異常資訊為: 
java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property 'methodInvoker' is not serializable: org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean 
解決辦法
 這是Spring的一個Bug,具體解決方案見http://jira.springframework.org/browse/SPR-3797,上面有解決的MethodInvokingJobDetailFactoryBean程式碼。 

要點3 還是NotSerializableException 
問題描述
 雖然MethodInvokingJobDetailFactoryBean的問題解決了,但是QuartzJob或者QuartzJob的屬性沒有實現Serializable介面(比如在QuartzJob中注入了DAO)。 
解決辦法3.1 如果是自己可以掌握的程式碼,可以為這些QuartzJob及其屬性都加上實現Serializable介面。 
解決辦法3.2 編寫一個實現Serializable介面,沒有屬性的QuartzJob,讓它從Spring容器(ApplicationContext)中獲取原來的那個QuartzJob的Bean,再呼叫原來QuartzJob的方法解決問題。該方法的問題在於怎麼獲取ApplicationContext,如果用ClassPathXmlApplicationContext,等於是另外建立了一個Spring容器(web.xml裡面定義的是另外一個)。 
解決辦法3.3 將原有的介面暴露,在Job中想辦法遠端呼叫該介面。 
這三種辦法各有好壞,現在也想不出其他更好的辦法,先用這些頂著吧。 

要點4 叢集之後把其中一個Quartz服務停了,其他的也不接手工作 
問題描述
 叢集之後,A節點執行了大多數任務,B節點大部分時間處於空閒,停掉A節點,B節點也不會接手工作。 
解決辦法 修改Quartz的配置,將每個節點的org.quartz.scheduler.instanceId設定為不同的值,或者都設定為AUTO。另外org.quartz.jobStore.isClustered屬性必須設為true,org.quartz.jobStore.clusterCheckinInterval屬性為叢集中每次檢查的時間間隔(按我的理解,應該差不多等於一個伺服器掛了之後,其他伺服器接手的時間),單位為毫秒,預設值是15000。 

要點5 MethodInvokingJobDetailFactoryBean幾個屬性的作用 
問題描述
 MethodInvokingJobDetailFactoryBean中concurrent和shouldRecover屬性的作用 
解釋 concurrent為true,則允許一個QuartzJob併發執行,否則就是順序執行。例如QuartzJob A執行時間為15秒,配置為每10秒執行一次;如果concurrent為true,則0秒的時候啟動一次A,10秒的時候再啟動一次A,20秒的時候再啟動一次A,不管前面啟動的A有沒有執行完;如果concurrent為false,則0秒的時候啟動一次A,15秒的時候A執行完畢,再第二次啟動A。 
shouldRecover屬性為true,則當Quartz服務被中止後,再次啟動或叢集中其他機器接手任務時會嘗試恢復執行之前未完成的所有任務。例如QuartzJob B,在每次00秒的時候啟動,假如在03:00的任務執行完之後伺服器1被中止,伺服器2在05:15的時候才接手;如果shouldRecover屬性為true,則伺服器2會嘗試著補回原來在04:00和05:00的時候應該做的任務,如果shouldRecover屬性為false,則伺服器2只會從06:00的時候再執行B。 

附件是一個Demo,使用資料庫為Oracle,建立資料庫表的SQL指令碼可以在Quartz的發行包(http://www.opensymphony.com/quartz/download.action)中\docs\dbTables目錄下找到。資料庫相關配置都在quartz.properties中,可根據實際需要自行修改。