In a cluster environment, you will encounter the persistent problem of how to coordinate automated jobs with Quartz across multiple apps.

Imagine that A, B, and C3 servers are simultaneously serving as cluster servers:

There is one QUARTZ on each machine A, B and C, and they will automatically perform their tasks according to the set SCHEDULE.

Let’s not say what it does, but it’s a bit like multithreading.

Then there will be the problem of “resource competition” in multi-threading, that is, dirty reads and dirty writes may occur. Since there is QUARTZ in all three APP Servers, there will be the phenomenon of repeated processing of tasks.

The general outside solution is to install QUARTZ on one APP and not the other two, so the cluster is virtually empty.

Another solution is to move code, which affects code already written for QUARTZ JOB, which can be painful for developers;

I took a close look at Spring’s structure and QUARTZ’s documentation and found a solution based on QUARTZ’s ability to instantiate data.

Advantages of this scheme:

1. QUARTZ can be deployed on each APP SERVER as a cluster point;

2. QUARTZ TASK (12 tables) instantiates such as database, database engine based and high-available policy (a policy of cluster) automatically coordinates QUARTZ of each node, when QUARTZ of any node is abnormal down or error, QUARTZ for the other nodes starts automatically;

3. There is no need for developers to change the original QUARTZ, using SPRING+ class reflection mechanism to reconstruct the original program;

I also did some research beforehand and found that all the solutions currently offered on GOOGLE or in various forums are either partial, wrong, too old, or completely copied from others.

In particular, when using QUARTZ+SPRING to instantiate a database object, it will throw errors (a BUG from SPRING). The current solutions on the web are all wrong or not mentioned at all.

Solution:

Mysql > instantiate A QUARTZ TASK into a database. QUARTZ can only be clustered if it is instantiated into a database. The quartz – 1.8.4 / docs/dbTables/tables_oracle SQL execution in ORACLE9I2 and above version it generates 12 tables;

2. Generate the Motion.properties file and place it in the SRC directory of the project so that it can be included in the class path when compiled.

Most of our developers like to use SPRING+QUARTZ, so we don’t have to write much about this. If you do not write quartz, you will call the quartz. Properties file in your jar package as the default properties file and modify the quartz.

The contents of the Quartz. XML file:

<? The XML version = “1.0” encoding = “utf-8”? >

<beans>

<bean id=”mapScheduler” lazy-init=”false” autowire=”no”

class=”org.springframework.scheduling.quartz.SchedulerFactoryBean”>

<property name=”configLocation” value=”classpath:quartz.properties” />

<property name=”triggers”>

<list>

<ref bean=”cronTrigger” />

</list>

</property>

<! Because the bean can only be refactored using class reflection

<property name=”applicationContextSchedulerContextKey” value=”applicationContext” />

</bean>

The contents of the Quartz. Properties file:

org.quartz.scheduler.instanceName = mapScheduler

org.quartz.scheduler.instanceId = AUTO

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate

org.quartz.jobStore.dataSource = myXADS

org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.jobStore.isClustered = true

org.quartz.dataSource.myXADS.jndiURL=jdbc/TestQuartzDS

org.quartz.dataSource.myXADS.jndiAlwaysLookup = DB_JNDI_ALWAYS_LOOKUP

org.quartz.dataSource.myXADS.java.naming.factory.initial = weblogic.jndi.WLInitialContextFactory

org.quartz.dataSource.myXADS.java.naming.provider.url = t3://localhost:7020

org.quartz.dataSource.myXADS.java.naming.security.principal = weblogic

org.quartz.dataSource.myXADS.java.naming.security.credentials = weblogic

3. Rewrite quartz’s QuartzJobBean class

Error “SerialIZABLE” when using Quartz + Spring to instantiate A Quartz task into a database

<bean id=”jobtask” class=”org.springframework.scheduling.quartz. MethodInvokingJobDetailFactoryBean “>

<property name=”targetObject”>

<ref bean=”quartzJob”/>

</property>

<property name=”targetMethod”>

<value>execute</value>

</property>

</bean>

The MethodInvokingJobDetailFactoryBean methodInvoking method in class, is does not support serialization, therefore in the QUARTZ TASK serialized into the database will be wrong. It is said on the Internet that taking the SPRING source code, modifying the solution, and then packaging it as Spring.jar is not a good method, it is not safe.

You must rewrite a class of your own based on QuartzJobBean, and then use SPRING to call the overridden class (we’ll call it “QuartzJobBean”) MyDetailQuartzJobBean) is injected into the appContext, and AOP technology is used to reflect the original quartzJobx(that is, the implementation class that the developer already made for executing QUARTZ jobs).

Here’s the MyDetailQuartzJobBean class:

public class MyDetailQuartzJobBean extends QuartzJobBean {

protected final Log logger = LogFactory.getLog(getClass());

private String targetObject;

private String targetMethod;

private ApplicationContext ctx;

protected void executeInternal(JobExecutionContext context)

throws JobExecutionException {

try {

logger.info(“execute [” + targetObject + “] at once>>>>>>”);

Object otargetObject = ctx.getBean(targetObject);

Method m = null;

try {

m = otargetObject.getClass().getMethod(targetMethod,

new Class[] {});

m.invoke(otargetObject, new Object[] {});

} catch (SecurityException e) {

logger.error(e);

} catch (NoSuchMethodException e) {

logger.error(e);

}

} catch (Exception e) {

throw new JobExecutionException(e);

}

}

public void setApplicationContext(ApplicationContext applicationContext){

this.ctx=applicationContext;

}

public void setTargetObject(String targetObject) {

this.targetObject = targetObject;

}

public void setTargetMethod(String targetMethod) {

this.targetMethod = targetMethod;

}

}

Take a look at the full Quartz. XML (note that the bold red is particularly important) :

<? The XML version = “1.0” encoding = “utf-8”? >

<beans>

<bean id=”mapScheduler” lazy-init=”false” autowire=”no”

class=”org.springframework.scheduling.quartz.SchedulerFactoryBean”>

<property name=”configLocation” value=”classpath:quartz.properties” />

<property name=”triggers”>

<list>

<ref bean=”cronTrigger” />

</list>

</property>

<property name=” applicationContextSchedulerContextKey ” value=” applicationContext ” />

</bean>

<bean id=”quartzJob” class=”com.testcompany.framework.quartz.QuartzJob”>

</bean>

<bean id=”jobTask” class=”org.springframework.scheduling.quartz.JobDetailBean”>

<property name=”jobClass”>

<value>com.testcompany.framework.quartz. MyDetailQuartzJobBean </value>

</property>

<property name=”jobDataAsMap”>

<map>

<entry key=”quartzJob” value=”quartzJob” />

<entry key=”targetMethod” value=”execute” />

</map>

</property>

</bean>

<bean id=”cronTrigger” class=”org.springframework.scheduling.quartz.CronTriggerBean”>

<property name=”jobDetail”>

<ref bean=”jobTask” />

</property>

<property name=”cronExpression”>

<value>0/5 * * * * ? </value>

</property>

</bean>

</beans>

Jar, quartz-all-1.8.4.jar, quartz-oracle-1.8.4.jar, and quartz-weblogic-1.8.4.jar in web-INF /lib.

Testing:

Several nodes have Quartz tasks, only one Quartz is running, and the quartz on the other nodes is not running.

Run QUARTZ (system.out.println(” execute once… ), the node running quartz will print execute once in the background. After about 7 seconds, the quartz of the other node automatically detects that the quartz instance running in the cluster has been shutdown. Therefore, the Quartz cluster will automatically start a Quartz Job on any available APP.

QUARTZ’s HA clustering is now complete, and we can configure QUARTZ’s clustering and automatic error redundancy without changing the original code.