Wednesday, December 16, 2009

Clustered Quartz Scheduler In 5 Minutes

Need a fast clustered/persistent Quartz Job Scheduler? Quartz Scheduler, the ubiquitous job scheduler built into Spring, JBoss and Grails can be configured to provide those features in under 5 minutes in this brief tutorial.

A Brief Digression Into The Why

Why do I need a persistent scaled out job scheduler? The main use cases for a clustered/persistent job scheduler are:
  • HA - You need to be able to restart your application without losing scheduled jobs
  • Scale Out - Your application now needs more than one node to handle the load it receives and you want your scheduled jobs to distribute across nodes.
  • You are using a database to persist and or scale your scheduler and you are seeing DB load/locking issues and or you find it two difficult to setup or maintain.

Steps:

1) Download Terracotta 3.2 (Which includes Quartz Scheduler) http://www.terracotta.org/dl/oss-download-catalog

2) Put the following jars in your class path (all included in the quartz-1.7.0 directory of the Terracotta kit from above):

quartz-1.7.0.jar - regular quartz jar
quartz-terracotta-1.0.0.jar - Terracotta clustered store

3) Whip up some scheduler code:
 public class QuartzSample {

public void startJobs() throws Exception {
Properties props = new Properties();
props.load(QuartzSample.class.getClassLoader().getResourceAsStream("org/quartz/quartz.properties"));


// **** Begin Required TC props
props.setProperty(StdSchedulerFactory.PROP_JOB_STORE_CLASS,"org.terracotta.quartz.TerracottaJobStore");
props.setProperty("org.quartz.jobStore.tcConfigUrl", "localhost:9510");
// *** End Required Terrocotta Stuff


StdSchedulerFactory factory = new StdSchedulerFactory(props);
Scheduler scheduler = factory.getScheduler();
scheduler.start();
if (scheduler.getJobDetail("myJob", "myGroup") == null) {
System.out.println("Scheduling Job!");
JobDetail jobDetail = new JobDetail("myJob", "myGroup", DumbJob.class);
Trigger trigger = TriggerUtils.makeSecondlyTrigger(5);
trigger.setName("myTrigger");
scheduler.scheduleJob(jobDetail, trigger);
} else {
System.out.println("Job Already Scheduled!");
}
}

public static class DumbJob implements Job {

@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("Works baby");
}

}

public static void main(String[] args) throws Exception {
new QuartzSample().startJobs();
}
** NOTE: Notice the two lines of properties that set things up for clustering with Terracotta in the sample. That's the only difference from single node unclustered Quartz.

4) Start the Terracotta server by running start

./start-tc-server.sh

in the bin directory of the Terracotta kit

5) Run the sample code above and watch it run the job every 5 seconds. Then kill the sample app and restart it. The app will tell you that the job is already scheduled and the job will continue.

Conclusion

Two lines of configuration and a server takes you from ubiquitous job scheduler built into Spring, JBoss and Grails to scale out and persistence.

Have fun!

4 comments:

pveentjer said...

Hi Steve,

looks cool and reduces the need to misuse a database for these kinds of volatile data.

Can this functionality be used without setting the bootclasspath stuff that normally is needed to use TC? (just like the Hibernate support)

Steve Harris said...

Yep, no bootjar. Just the above stuff.

Taylor said...

Any dev console luv for quartz?

Steve Harris said...

We have a prototype in house. Highly likely we'll productize it.