單獨的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配置文件的位置。
- <bean id="quartzJobFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
- <property name="triggers">
- <list>
- <ref bean="clusterTesterJobScheduledTask" />
- </list>
- </property>
- <property name="configLocation" value="classpath:quartz.properties" />
- </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中,可根據實際需要自行修改。