Wednesday 6 July 2011

Setting up Quartz for cluster application

Quartz is used to run a task at scheduled time on regular basis. In our system, we have used quartz to reverse the pending transactions after a specified time if its not completed.

Our production application server runs on a cluster (two instances of glassfish). If quartz is not set up properly, all the instances will be invoking quartz scheduler. I wanted my scheduler to be invoked only once (by any of the one server).

This can be done by using quartz tables which basically make sure that a task invokes only once.

Follow the below steps to setting up quartz scheduler tasks (either on one instance or on cluster):

1. Create schedulerTask for SchedulerFactoryBean as follows.
<bean id="scheduleTask" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="applicationContextSchedulerContextKey">
    <value>applicationContext</value></property>
  <property name="triggers">
    <list>
      <ref bean="scheduleTaskCron" />
    </list>
  </property>
  <property name="overwriteExistingJobs" value="true" />
  <property name="quartzProperties">
    <props>
      <prop key="org.quartz.scheduler.instanceName">RejectBatchScheduler</prop>
      <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
      <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
      <prop key="org.quartz.jobStore.class">
org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
      <prop key="org.quartz.jobStore.driverDelegateClass">
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate</prop>
      <prop key="org.quartz.jobStore.tablePrefix">qrtz_</prop>
      <prop key="org.quartz.jobStore.isClustered">true</prop>
      <prop key="org.quartz.threadPool.class">
org.quartz.simpl.SimpleThreadPool</prop>
      <prop key="org.quartz.threadPool.threadCount">25</prop>
      <prop key="org.quartz.threadPool.threadPriority">5</prop>
    </props>
  </property>
</bean>

Note: The important thing for cluster is to define the quartzProperties. In case the task does not need to run on cluster then no need to define the quartzProperties and no need to create the tables.
2. Define the cron trigger (scheduleTaskCron), specifying the cron expression.
<bean id="scheduleTaskCron" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0 3 * * ?" />
bean>

3. Create a JobDetailBean injecting the scheduleTasks as maps as follows
<bean name="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.javaworkbench.scheduler.ScheduleTask" />
<property name="jobDataAsMap">
<map>
<entry key="rejectPendingTransferTaskJob" value="rejectPendingTransferTask"/>
map>
property>
bean>

4. And now the bean definition for “rejectPendingTransferTask
<bean name="rejectPendingTransferTask" class="com.javaworkbench.scheduler.RejectPendingTransferTask"/>

5. ScheduleTask has to extend QuatzJobBean which will be serialized and inserted into the tables to make sure it runs only by one server.

6. Execute your appropriate database scripts. They can be found in the quartz distribution under the docs\dbTables subdirectory. The code other then yellow in colour is a standard code and properties in yellow colour will be changing as per your project.

References:
http://toddkaufman.blogspot.com/2008/09/clustered-scheduling-with-spring-and.html

3 comments:

  1. You should not need to set the property "org.quartz.jobStore.class" because Spring automatically sets it to "org.springframework.scheduling.quartz.LocalDataSourceJobStore" in the SchedulerFactoryBean if dataSource is not null.

    ReplyDelete
  2. Thông tin rất bổ ích, cảm ơn bạn đã share.
    Xem thêm website : Giá đá thạch anh

    ReplyDelete
  3. Bài viết của Bạn rất hữu ích, thank bạn đã share.
    Xem tại website : Đá thạch anh vụn

    ReplyDelete