problems that may occur in a clustered environment
In the previous blog we introduced how to add the quartz timing scheduling engine from scratch in your project, which is a quartz and spring integration process, it is easy to implement, but we are now in the enterprise project is usually deployed in the cluster environment, In this way we have the timing of scheduling problems, because our scheduled tasks are loaded in memory, each cluster node in the scheduler will be executed, there will be repeated execution and resource competition problems, then how to solve such problems, look down on it ... Solution Solutions
In general enterprises to solve similar problems are generally deployed on a note quartz Other note is not deployed (or in several other machines plus IP address filtering), but so the cluster for the scheduled task is meaningless, and there is a single point of failure of the hidden danger, That is, when the machine that deploys the quartz is hung up, our scheduled tasks stop serving, which is definitely not what we want.
Quartz itself is a support cluster, we solve this problem by quartz cluster way. Quartz cluster
While a single Quartz instance can give you a good job scheduling capability, it does not allow typical enterprise requirements, such as scalability, high reliability, to be met. If you need the ability to fail over and run an ever-increasing number of job,quartz clusters, it's bound to be part of your dialect, and even if one of the machines crashes at the worst of times, it ensures that all jobs are executed.
Quartzjob Scheduling Framework
Understanding the benefits of the quartz cluster, the next step is to transform our previous project to add quartz cluster features.
Quartz a node in a cluster relies on a database to propagate the state of a Scheduler instance, you can only apply Quartz clusters when using JDBC Jobstore
So the first step in our cluster is to build the 12 tables that are needed for quartz 1, to build a table
Build the related table structure in the quartz core package with the built-in statements provided by quartz
The resulting table structure is as follows
These tables are used to store information such as task information, triggers, schedulers, cluster nodes, etc.
Detailed Explanation:
Qrtz_calendars storing quartz calendar information in BLOB type
Qrtz_cron_triggers storage cron Trigger, including cron expressions and time zone information
Qrtz_fired_triggers stores state information related to triggered trigger and execution information for the associated job
Qrtz_paused_trigger_grps storing information for a paused TRIGGER group
Qrtz_scheduler_state stores a small amount of state information about the SCHEDULER, and other SCHEDULER instances (if used in a cluster)
Qrtz_locks the non-locking information of the stored program (if a pessimistic lock is used)
Qrtz_job_details store details for each configured JOB
Qrtz_job_listeners storing information about configured Joblistener
Qrtz_simple_triggers stores simple trigger, including number of repetitions, intervals, and number of touches
Qrtz_blog_triggers Trigger as BLOB type storage (for quartz users to create their own custom Trigger types with JDBC, Jobstore does not know how to store instances)
Qrtz_trigger_listeners storing information for configured Triggerlistener
Qrtz_triggers storing information for configured trigger
All tables default to the previous prefix qrtz_ start. Modifications can be configured by the Quartz.properties (org.quartz.jobstore.tableprefix= qrtz_). 2. Writing quartz.properties files
set up the quartz.properties file to put it in the SRC directory of the project, as follows:
#============================================================================ # Configure Main Scheduler Properties #============================================================================ Org.quartz.scheduler.instanceName = Mscheduler org.quartz.scheduler.instanceId = AUTO org.quartz.jobstore.clustercheckininterval=20000 #============================================================================ # Configure ThreadPool #============================================================================ Org.quartz.threadPool.class = Org.quartz.simpl.SimpleThreadPool Org.quartz.threadPool.threadCount = 3 Org.quartz.threadPool.threadPriority = 5 #============================================================================ # Configure Jobstore #============================================================================ #org. Quartz.jobStore.class = Org.quartz.simpl.RAMJobStore Org.quartz.jobStore.class = Org.quartz.impl.jdbcjobstore.JobStoreTX #org. Quartz.jobStore.driverDelegateClass = Org.quartz.impl.jdbcjobstore.StdJDBCDelegate Org.quartz.jobStore.useProperties = True #org. Quartz.jobStore.dataSource = MyDS Org.quartz.jobStore.tablePrefix = Qrtz_ org.quartz.jobStore.isClustered = True Org.quartz.jobstore.maxmisfirestohandleatatime=1 #============================================================================ # Configure DataSources #============================================================================ #mysql #org. Quartz.dataSource.myDS.driver = Com.ibm.db2.jcc.DB2Driver #org. Quartz.dataSource.myDS.URL = jdbc:db2://localhost:50000/db #org. Quartz.dataSource.myDS.user = DB2 #org. Quartz.dataSource.myDS.password = DB2 #org. quartz.dataSource.myDS.maxConnections = 5 #oracle #org. Quartz.dataSource.myDS.driver = Oracle.jdbc.driver.OracleDriver #org. Quartz.dataSource.myDS.URL = Jdbc:oracle:thin: @localhost: 1521:ORCL #org. Quartz.dataSource.myDS.user = Scott #org. Quartz.dataSource.myDS.password = Shao #org. quartz.dataSource.myDS.maxConnections = 5 #For Tomcat Org.quartz.jobStore.driverDelegateClass =org.quartz.impl.jdbcjobstore.oracle.oracledelegate #For Weblogic & Websphere #org. Quartz.jobStore.driverDelegateClass = Org.quartz.impl.jdbcjobstore.WebLogicDelegate Org.quartz.jobStore.useProperties = False Org.quartz.jobStore.dataSource = MyDS #JNDI MODE #For Tomcat Org.quartz.datasource.myds.jndiurl=java:comp/env/jdbc/oracle #For Weblogic & Websphere #org. quartz.datasource.myds.jndiurl=jdbc/oracle #============================================================================ # Configure Plugins #============================================================================ #org. Quartz.plugin.triggHistory.class = Org.quartz.plugins.history.LoggingJobHistoryPlugin #org. Quartz.plugin.jobInitializer.class = Org.quartz.plugins.xml.JobInitializationPlugin #org. Quartz.plugin.jobInitializer.fileNames = Jobs.xml #org. Quartz.plugin.jobInitializer.overWriteExistingJobs = True #org. Quartz.plugin.jobInitializer.failOnFileNotFound = True #org. Quartz.plugin.jobInitializer.scanInterval = 10 #org. Quartz.plugin.jobInitializer.wrapInUserTransaction = False |
The red bold part is the configuration required by the cluster
The core configuration is explained as follows:
The Org.quartz.jobStore.class property is Jobstoretx,
Persist the task to the data. Because nodes in a cluster rely on databases to propagate the state of scheduler instances, you can only apply quartz clusters when using JDBC Jobstore.
The Org.quartz.jobStore.isClustered property is true to notify the scheduler instance that it participates in a cluster.
Org.quartz.jobStore.clusterCheckinInterval
The Scheduler property defines the frequency, in milliseconds, that an instance of a sample is checked into the database.
Scheduler Check if other instances have not been checked in when they should be checked in;
This can indicate a failed scheduler instance, and the current scheduler will take over any job that fails and is recoverable.
With the check-in operation, Scheduler also updates its own status record. The smaller the clusterchedkininterval, the more frequently the scheduler node checks for scheduler instances that fail. The default value is 20000 (that is, 20 seconds)
3. Modify the Spring-time.xml file
<?xmlversion= "1.0" encoding= "UTF-8"?> <! Doctypebeanspublic "-//spring//dtd bean//en" "Http://www.springframework.org/dtd/spring-beans.dtd" > <beans> <!--scheduler lazy-init= ' false ' then the container starts executing the scheduler-- <beanid= "Startquertz" lazy-init= "false" autowire= "no" class= " Org.springframework.scheduling.quartz.SchedulerFactoryBean "> <property name= "DataSource" ref= "DataSource"/> <property name= "configlocation" value= "classpath:quartz.properties"/> <propertyname= "Triggers" > <list> <refbean= "Dotime"/> </list> </property> <!--allows you to use the Spring instance factory in the quartz context-- <propertyname= "Applicationcontextschedulercontextkey" value= "ApplicationContext"/> </bean> <!--triggers-- <beanid= "Dotime" class= "Org.springframework.scheduling.quartz.CronTriggerBean" > <propertyname= "Jobdetail" ref= "Jobtask" ></property> <!--cron Expressions-- <propertyname= "Cronexpression" value= "10,15,20,25,30,35,40,45,50,55 * * * *?" ></property> </bean> <!--tasks-- <beanid= "Jobtask" class= "Org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" > <propertyname= "TargetObject" ref= "Synusersjob" ></property> <propertyname= "Targetmethod" value= "Execute" ></property> </bean> <!--the work class to invoke-- <beanid= "Synusersjob" class= "Org.leopard.core.quartz.job.SynUsersJob" ></bean> </beans> |
Add red Bold Part code, inject data source and load Quartz.properties file
OK Quartz cluster configuration Only these steps, let's start the project ...
We're starting to open up .... Error!
17:00:59,718 ERROR contextloader:215-context initialization failed Org.springframework.beans.factory.BeanCreationException:Error creating Bean with Name ' Startquertz ' defined in Class path resource [Config/spring/spring-time.xml]: Invocation of Init method failed; Nested exception is org.quartz.JobPersistenceException:Couldn ' t store job:unable to serialize Jobdatamap for insertion in To database because the value of "MethodInvoker" is not Serializable:org.springframework.scheduling.quartz.Meth Odinvokingjobdetailfactorybean [see nested exception:java.io.NotSerializableException:Unable to serialize Jobdatamap For insertion to database because the value of property ' MethodInvoker ' was not Serializable:org.springframework.schedul Ing.quartz.MethodInvokingJobDetailFactoryBean] |
We mainly look at the red part, the main reason is that the MethodInvoking method in the Methodinvokingjobdetailfactorybean class is not serializable, so in the quartz task This serializable error is thrown when serializing into the database
4, solve serializable error solution
Check the Internet, to solve this problem, there are two main options: 4.1. Change the source code of Spring
Blog Address: http://jira.springframework.org/browse/SPR-3797
The author rewrote the Methodinvokingjobdetailfactorybean
4.2. Facet refactoring of Spring source through AOP reflection
Blog Address: http://blog.csdn.net/lifetragedy/article/details/6212831
Rewrite your own class according to Quartzjobbean, and then use SPRING to inject the overridden class (we name it: Mydetailquartzjobbean) into AppContext, and then use AOP technology to reflect the original Quartzjo BX (The execution class that the developer has already done to perform the QUARTZ job).
two ways I have been tested, can solve the problem, we here first solve this bug in the second way, did not modify any spring source 4.2.1, increase Mydetailquartzjobbean.java
Package Org.leopard.core.quartz; Import Java.lang.reflect.Method; Import Org.apache.commons.logging.Log; Import Org.apache.commons.logging.LogFactory; Import Org.quartz.JobExecutionContext; Import org.quartz.JobExecutionException; Import Org.springframework.context.ApplicationContext; Import Org.springframework.scheduling.quartz.QuartzJobBean; /** * Solve spring and quartz integration bug * */ 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; } } |
5, re-modify the Spring-time.xml file to solve the serializable problem
The contents of the modified Spring-time.xml file are as follows:
<?xmlversion= "1.0" encoding= "UTF-8"?> <! Doctypebeanspublic "-//spring//dtd bean//en" "/http/ Www.springframework.org/dtd/spring-beans.dtd "> <beans> <!--scheduler lazy-init= ' false ' then the container starts executing the scheduler-- <beanid= "Startquertz" lazy-init= " False "Autowire=" no "class=" Org.springframework.scheduling.quartz.SchedulerFactoryBean "> <propertyname= "DataSource" ref= "DataSource"/> <propertyname= "configlocation" value= "classpath:quartz.properties"/> <propertyname= |