zoukankan      html  css  js  c++  java
  • Spring+quartz 实现定时任务job集群配置

    为什么要有集群定时任务?

    因为如果多server都触发相同任务,又同时执行,那在99%的场景都是不适合的.比如银行每晚24:00都要汇总营业额.像下面3台server同时进行汇总,最终计算结果可能是真实结果的3倍,那对银行来说是无法想象的,完全不可接受.

    集群定时任务工作原理

    所以为了解决以上问题,每个server把将要及正在运行的job所有状态都即时同步到中央数据库,然后再次触发调用时从数据库中分析是否已有别的server正在运行相同job (同名同定时时间点的job属于相当job),如果相同job正在别的server运行,那么当前server忽略本次运行的job.

    Quartz的 Task(11 张表)实例化采用数据库存储,基于数据库引擎及 High-Available 的策略(集群的一种策略)自动协调每个节点的 Quartz。

    相关下载

    quartz-1.8.6

    项目源码样例 

    git地址: https://git.oschina.net/KingBoBo/SpringQuartzCluster.git

    相关文件

    ClusterJob.java

    说明:集群下工作的Job,仅是实现了Serializable的Bean.

    复制代码
    package king.quartz.job;
    

    import java.io.Serializable;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;

    /**

    • 可序列化的集群job
    • @author King

    */
    public class ClusterJob implements Serializable {
    public static int i = 0;

    </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> Log log = LogFactory.getLog(ClusterJob.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
    
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> execute() <span style="color: #0000ff;">throws</span><span style="color: #000000;"> InterruptedException {
        log.debug(</span>"begin begin begin begin begin begin begin begin ! job = "+ ++<span style="color: #000000;">i);
        System.out.print(</span>"job = "+i+" is running "<span style="color: #000000;">);
        </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j = 1 ; j &lt;= 5;j++){<span style="color: #008000;">//</span><span style="color: #008000;">运行5秒</span>
            System.out.print( j +" "<span style="color: #000000;">);
            Thread.sleep(</span>1*1000<span style="color: #000000;">);
        }
        System.out.println();
        log.debug(</span>"end end end end end end end end end end end end ! job = "+<span style="color: #000000;"> i);
    }
    

    }

    复制代码

    被修改后的MethodInvokingJobDetailFactoryBean

    因为spring自带的org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean的invoke()方法不能序列化job,导致无法把序列化后的信息写到表中,这可以算是spring的一个bug.

    所以有民间大神修改后的MethodInvokingJobDetailFactoryBean,该Bean的invoke()方法可使job序列化,2拿来直接上手用吧(注意按包路径要和applicationContext-quartz.xml文件中的路径一致)

    package king.springframework.scheduling.quartz;
    

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.quartz.Job;
    import org.quartz.JobDetail;
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.quartz.Scheduler;
    import org.quartz.StatefulJob;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.util.MethodInvoker;

    /**

    • 该类可让生成的job可以序列化到数据库中,Spring自带的MethodInvokingJobDetailFactoryBean却不能序列化生成的job

    • This is a cluster safe Quartz/Spring FactoryBean implementation, which produces a JobDetail implementation that can invoke any no-arg method on any Class.

    • <p>

    • Use this Class instead of the MethodInvokingJobDetailBeanFactory Class provided by Spring when deploying to a web environment like Tomcat.

    • <p>

    • <b>Implementation</b><br>

    • Instead of associating a MethodInvoker with a JobDetail or a Trigger object, like Spring's MethodInvokingJobDetailFactoryBean does, I made the [Stateful]MethodInvokingJob, which is not persisted in the database, create the MethodInvoker when the [Stateful]MethodInvokingJob is created and executed.

    • <p>

    • A method can be invoked one of several ways:

    • <ul>

    • <li>The name of the Class to invoke (targetClass) and the static method to invoke (targetMethod) can be specified.

    • <li>The Object to invoke (targetObject) and the static or instance method to invoke (targetMethod) can be specified (the targetObject must be Serializable when concurrent=="false").

    • <li>The Class and static Method to invoke can be specified in one property (staticMethod). example: staticMethod = "example.ExampleClass.someStaticMethod"

    • <br><b>Note:</b> An Object[] of method arguments can be specified (arguments), but the Objects must be Serializable if concurrent=="false".

    • </ul>

    • <p>

    • I wrote MethodInvokingJobDetailFactoryBean, because Spring's MethodInvokingJobDetailFactoryBean does not produce Serializable

    • JobDetail objects, and as a result cannot be deployed into a clustered environment like Tomcat (as is documented within the Class).

    • <p>

    • <b>Example</b>

    • <code>

    • <ul>

    • &amp;lt;bean id="&lt;i&gt;exampleTrigger&lt;/i&gt;" class="org.springframework.scheduling.quartz.CronTriggerBean"&amp;gt;
      
    • <ul>
      <i>&lt;!-- Execute example.ExampleImpl.fooBar() at 2am every day --&gt;</i><br>
      &lt;property name="<a href="http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html">cronExpression</a>" value="0 0 2 * * ?" /&gt;<br>
      &lt;property name="jobDetail"&gt;
      <ul>
      &lt;bean class="frameworkx.springframework.scheduling.quartz.<b>MethodInvokingJobDetailFactoryBean</b>"&gt;
      <ul>
      &lt;property name="concurrent" value="<i>false</i>"/&gt;<br>
      &lt;property name="targetClass" value="<i>example.ExampleImpl</i>" /&gt;<br>
      &lt;property name="targetMethod" value="<i>fooBar</i>" /&gt;
      </ul>
      &lt;/bean&gt;
      </ul>
      &lt;/property&gt;
      </ul>
      &lt;/bean&gt;
      <p>
      &lt;bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"&gt;
      <ul>
      &lt;property name="triggers"&gt;
      <ul>
      &lt;list&gt;
      <ul>
      &lt;ref bean="<i>exampleTrigger</i>" /&gt;
      </ul>
      &lt;/list&gt;
      </ul>
      &lt;/property&gt;
      </ul>
      &lt;/bean&gt;
      </ul>

    • </code>

    • In this example we created a MethodInvokingJobDetailFactoryBean, which will produce a JobDetail Object with the jobClass property set to StatefulMethodInvokingJob.class (concurrent"false"; Set to MethodInvokingJob.class when concurrent"true"), which will in turn invoke the static <code>fooBar</code>() method of the "<code>example.ExampleImpl</code>" Class. The Scheduler is the heart of the whole operation; without it, nothing will happen.

    • <p>

    • For more information on <code>cronExpression</code> syntax visit <a href="http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html">http://www.opensymphony.com/quartz/api/org/quartz/CronTrigger.html</a>

    • @author Stephen M. Wick

    • @see #afterPropertiesSet()
      */
      public class MethodInvokingJobDetailFactoryBean implements FactoryBean, BeanNameAware, InitializingBean
      {
      private Log logger = LogFactory.getLog(getClass());

      /**

      • The JobDetail produced by the <code>afterPropertiesSet</code> method of this Class will be assigned to the Group specified by this property. Default: Scheduler.DEFAULT_GROUP
      • @see #afterPropertiesSet()
      • @see Scheduler#DEFAULT_GROUP
        */
        private String group = Scheduler.DEFAULT_GROUP;

      /**

      • Indicates whether or not the Bean Method should be invoked by more than one Scheduler at the specified time (like when deployed to a cluster, and/or when there are multiple Spring ApplicationContexts in a single JVM<i> - Tomcat 5.5 creates 2 or more instances of the DispatcherServlet (a pool), which in turn creates a separate Spring ApplicationContext for each instance of the servlet</i>)
      • <p>
      • Used by <code>afterPropertiesSet</code> to set the JobDetail.jobClass to MethodInvokingJob.class or StatefulMethodInvokingJob.class when true or false, respectively. Default: true
      • @see #afterPropertiesSet()
        */
        private boolean concurrent = true;

      /** Used to set the JobDetail.durable property. Default: false

      /**

      • Used by <code>afterPropertiesSet</code> to set the JobDetail.volatile property. Default: false
      • <p>Volatility - if a job is volatile, it is not persisted between re-starts of the Quartz scheduler.
      • <p>I set the default to false to be the same as the default for a Quartz Trigger. An exception is thrown
      • when the Trigger is non-volatile and the Job is volatile. If you want volatility, then you must set this property, and the Trigger's volatility property, to true.
      • @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
      • @see #afterPropertiesSet()
        */
        private boolean volatility = false;

      /**

      • Used by <code>afterPropertiesSet</code> to set the JobDetail.requestsRecovery property. Default: false<BR>
      • <p>RequestsRecovery - if a job "requests recovery", and it is executing during the time of a 'hard shutdown' of the scheduler (i.e. the process it is running within crashes, or the machine is shut off), then it is re-executed when the scheduler is started again. In this case, the JobExecutionContext.isRecovering() method will return true.
      • @see <a href="http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html">http://www.opensymphony.com/quartz/wikidocs/TutorialLesson3.html</a>
      • @see #afterPropertiesSet()
        */
        private boolean shouldRecover = false;

      /**

      • A list of names of JobListeners to associate with the JobDetail object created by this FactoryBean.
      • @see #afterPropertiesSet()
        /
        private String[] jobListenerNames;

      /** The name assigned to this bean in the Spring ApplicationContext.

      • Used by <code>afterPropertiesSet</code> to set the JobDetail.name property.
      • @see afterPropertiesSet()
      • @see JobDetail#setName(String)
        /
        private String beanName;

      /**

      • The JobDetail produced by the <code>afterPropertiesSet</code> method, and returned by the <code>getObject</code> method of the Spring FactoryBean interface.
      • @see #afterPropertiesSet()
      • @see #getObject()
      • @see FactoryBean
        /
        private JobDetail jobDetail;

      /**

      • The name of the Class to invoke.
        /
        private String targetClass;

      /**

      • The Object to invoke.
      • <p>
      • {@link #targetClass} or targetObject must be set, but not both.
      • <p>
      • This object must be Serializable when {@link #concurrent} is set to false.
        */
        private Object targetObject;

      /**

      • The instance method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
      • <p>
      • targetMethod or {@link #staticMethod} should be set, but not both.
        /
        private String targetMethod;

      /**

      • The static method to invoke on the Class or Object identified by the targetClass or targetObject property, respectfully.
      • <p>
      • {@link #targetMethod} or staticMethod should be set, but not both.
        */
        private String staticMethod;

      /**

      • Method arguments provided to the {@link #targetMethod} or {@link #staticMethod} specified.
      • <p>
      • All arguments must be Serializable when {@link #concurrent} is set to false.
      • <p>
      • I strongly urge you not to provide arguments until Quartz 1.6.1 has been released if you are using a JDBCJobStore with
      • Microsoft SQL Server. There is a bug in version 1.6.0 that prevents Quartz from Serializing the Objects in the JobDataMap
      • to the database. The workaround is to set the property "org.opensymphony.quaryz.useProperties = true" in your quartz.properties file,
      • which tells Quartz not to serialize Objects in the JobDataMap, but to instead expect all String compliant values.
        */
        private Object[] arguments;

      /**

      • Get the targetClass property.
      • @see #targetClass
      • @return targetClass
        */
        public String getTargetClass()
        {
        return targetClass;
        }

      /**

      • Set the targetClass property.
      • @see #targetClass
        */
        public void setTargetClass(String targetClass)
        {
        this.targetClass = targetClass;
        }

      /**

      • Get the targetMethod property.
      • @see #targetMethod
      • @return targetMethod
        */
        public String getTargetMethod()
        {
        return targetMethod;
        }

      /**

      • Set the targetMethod property.
      • @see #targetMethod
        */
        public void setTargetMethod(String targetMethod)
        {
        this.targetMethod = targetMethod;
        }

      /**

      • @return jobDetail - The JobDetail that is created by the afterPropertiesSet method of this FactoryBean
      • @see #jobDetail
      • @see #afterPropertiesSet()
      • @see FactoryBean#getObject()
        */
        public Object getObject() throws Exception
        {
        return jobDetail;
        }

      /**

      • @return JobDetail.class
      • @see FactoryBean#getObjectType()
        */
        public Class getObjectType()
        {
        return JobDetail.class;
        }

      /**

      • @return true
      • @see FactoryBean#isSingleton()
        */
        public boolean isSingleton()
        {
        return true;
        }

      /**

      • Set the beanName property.
      • @see #beanName
      • @see BeanNameAware#setBeanName(String)
        */
        public void setBeanName(String beanName)
        {
        this.beanName = beanName;
        }

      /**

      • Invoked by the Spring container after all properties have been set.

      • <p>

      • Sets the <code>jobDetail</code> property to a new instance of JobDetail

      • <ul>

      • <li>jobDetail.name is set to <code>beanName</code><br>

      • <li>jobDetail.group is set to <code>group</code><br>

      • <li>jobDetail.jobClass is set to MethodInvokingJob.class or StatefulMethodInvokingJob.class depending on whether the <code>concurrent</code> property is set to true or false, respectively.<br>

      • <li>jobDetail.durability is set to <code>durable</code>

      • <li>jobDetail.volatility is set to <code>volatility</code>

      • <li>jobDetail.requestsRecovery is set to <code>shouldRecover</code>

      • <li>jobDetail.jobDataMap["targetClass"] is set to <code>targetClass</code>

      • <li>jobDetail.jobDataMap["targetMethod"] is set to <code>targetMethod</code>

      • <li>Each JobListener name in <code>jobListenerNames</code> is added to the <code>jobDetail</code> object.

      • </ul>

      • <p>

      • Logging occurs at the DEBUG and INFO levels; 4 lines at the DEBUG level, and 1 line at the INFO level.

      • <ul>

      • <li>DEBUG: start

      • <li>DEBUG: Creating JobDetail <code>{beanName}</code>

      • <li>DEBUG: Registering JobListener names with JobDetail object <code>{beanName}</code>

      • <li>INFO: Created JobDetail: <code>{jobDetail}</code>; targetClass: <code>{targetClass}</code>; targetMethod: <code>{targetMethod}</code>;

      • <li>DEBUG: end

      • </ul>

      • @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()

      • @see JobDetail

      • @see #jobDetail

      • @see #beanName

      • @see #group

      • @see MethodInvokingJob

      • @see StatefulMethodInvokingJob

      • @see #durable

      • @see #volatility

      • @see #shouldRecover

      • @see #targetClass

      • @see #targetMethod

      • @see #jobListenerNames
        */
        public void afterPropertiesSet() throws Exception
        {
        try
        {
        logger.debug(
        "start");

         logger.debug(</span>"Creating JobDetail "+<span style="color: #000000;">beanName);
         jobDetail </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> JobDetail();
         jobDetail.setName(beanName);
         jobDetail.setGroup(group);
         jobDetail.setJobClass(concurrent </span>? MethodInvokingJob.<span style="color: #0000ff;">class</span> : StatefulMethodInvokingJob.<span style="color: #0000ff;">class</span><span style="color: #000000;">);
         jobDetail.setDurability(durable);
         jobDetail.setVolatility(volatility);
         jobDetail.setRequestsRecovery(shouldRecover);
         </span><span style="color: #0000ff;">if</span>(targetClass!=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
             jobDetail.getJobDataMap().put(</span>"targetClass"<span style="color: #000000;">, targetClass);
         </span><span style="color: #0000ff;">if</span>(targetObject!=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
             jobDetail.getJobDataMap().put(</span>"targetObject"<span style="color: #000000;">, targetObject);
         </span><span style="color: #0000ff;">if</span>(targetMethod!=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
             jobDetail.getJobDataMap().put(</span>"targetMethod"<span style="color: #000000;">, targetMethod);
         </span><span style="color: #0000ff;">if</span>(staticMethod!=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
             jobDetail.getJobDataMap().put(</span>"staticMethod"<span style="color: #000000;">, staticMethod);
         </span><span style="color: #0000ff;">if</span>(arguments!=<span style="color: #0000ff;">null</span><span style="color: #000000;">)
             jobDetail.getJobDataMap().put(</span>"arguments"<span style="color: #000000;">, arguments);
         
         logger.debug(</span>"Registering JobListener names with JobDetail object "+<span style="color: #000000;">beanName);
         </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.jobListenerNames != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
             </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0; i &lt; <span style="color: #0000ff;">this</span>.jobListenerNames.length; i++<span style="color: #000000;">) {
                 </span><span style="color: #0000ff;">this</span>.jobDetail.addJobListener(<span style="color: #0000ff;">this</span><span style="color: #000000;">.jobListenerNames[i]);
             }
         }
         logger.info(</span>"Created JobDetail: "+jobDetail+"; targetClass: "+targetClass+"; targetObject: "+targetObject+"; targetMethod: "+targetMethod+"; staticMethod: "+staticMethod+"; arguments: "+arguments+";"<span style="color: #000000;">);
        

        }
        finally
        {
        logger.debug(
        "end");
        }
        }

      /**

      • Setter for the concurrent property.
      • @param concurrent
      • @see #concurrent
        */
        public void setConcurrent(boolean concurrent)
        {
        this.concurrent = concurrent;
        }

      /**

      • setter for the durable property.
      • @param durable
      • @see #durable
        */
        public void setDurable(boolean durable)
        {
        this.durable = durable;
        }

      /**

      • setter for the group property.
      • @param group
      • @see #group
        */
        public void setGroup(String group)
        {
        this.group = group;
        }

      /**

      • setter for the {@link #jobListenerNames} property.
      • @param jobListenerNames
      • @see #jobListenerNames
        */
        public void setJobListenerNames(String[] jobListenerNames)
        {
        this.jobListenerNames = jobListenerNames;
        }

      /**

      • setter for the {@link #shouldRecover} property.
      • @param shouldRecover
      • @see #shouldRecover
        */
        public void setShouldRecover(boolean shouldRecover)
        {
        this.shouldRecover = shouldRecover;
        }

      /**

      • setter for the {@link #volatility} property.
      • @param volatility
      • @see #volatility
        */
        public void setVolatility(boolean volatility)
        {
        this.volatility = volatility;
        }

      /**

      • This is a cluster safe Job designed to invoke a method on any bean defined within the same Spring

      • ApplicationContext.

      • <p>

      • The only entries this Job expects in the JobDataMap are "targetClass" and "targetMethod".<br>

        • It uses the value of the <code>targetClass</code> entry to get the desired bean from the Spring ApplicationContext.<br>
        • It uses the value of the <code>targetMethod</code> entry to determine which method of the Bean (identified by targetClass) to invoke.
      • <p>

      • It uses the static ApplicationContext in the MethodInvokingJobDetailFactoryBean,

      • which is ApplicationContextAware, to get the Bean with which to invoke the method.

      • <p>

      • All Exceptions thrown from the execute method are caught and wrapped in a JobExecutionException.

      • @see MethodInvokingJobDetailFactoryBean#applicationContext

      • @see #execute(JobExecutionContext)

      • @author Stephen M. Wick
        */
        public static class MethodInvokingJob implements Job
        {
        protected Log logger = LogFactory.getLog(getClass());

        /**

        • When invoked by a Quartz scheduler, <code>execute</code> invokes a method on a Class or Object in the JobExecutionContext provided.

        • <p>

        • <b>Implementation</b><br>

        • The Class is identified by the "targetClass" entry in the JobDataMap of the JobExecutionContext provided. If targetClass is specified, then targetMethod must be a static method.<br>

        • The Object is identified by the 'targetObject" entry in the JobDataMap of the JobExecutionContext provided. If targetObject is provided, then targetClass will be overwritten. This Object must be Serializable when <code>concurrent</code> is set to false.<br>

        • The method is identified by the "targetMethod" entry in the JobDataMap of the JobExecutionContext provided.<br>

        • The "staticMethod" entry in the JobDataMap of the JobExecutionContext can be used to specify a Class and Method in one entry (ie: "example.ExampleClass.someStaticMethod")<br>

        • The method arguments (an array of Objects) are identified by the "arguments" entry in the JobDataMap of the JobExecutionContext. All arguments must be Serializable when <code>concurrent</code> is set to false.

        • <p>

        • Logging is provided at the DEBUG and INFO levels; 8 lines at the DEBUG level, and 1 line at the INFO level.

        • @see Job#execute(JobExecutionContext)
          */
          public void execute(JobExecutionContext context) throws JobExecutionException
          {
          try
          {
          logger.debug(
          "start");
          String targetClass
          = context.getMergedJobDataMap().getString("targetClass");
          logger.debug(
          "targetClass is "+targetClass);
          Class targetClassClass
          = null;
          if(targetClass!=null)
          {
          targetClassClass
          = Class.forName(targetClass); // Could throw ClassNotFoundException
          }
          Object targetObject
          = context.getMergedJobDataMap().get("targetObject");
          logger.debug(
          "targetObject is "+targetObject);
          String targetMethod
          = context.getMergedJobDataMap().getString("targetMethod");
          logger.debug(
          "targetMethod is "+targetMethod);
          String staticMethod
          = context.getMergedJobDataMap().getString("staticMethod");
          logger.debug(
          "staticMethod is "+staticMethod);
          Object[] arguments
          = (Object[])context.getMergedJobDataMap().get("arguments");
          logger.debug(
          "arguments are "+arguments);

           logger.debug(</span>"creating MethodInvoker"<span style="color: #000000;">);
           MethodInvoker methodInvoker </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> MethodInvoker();
           methodInvoker.setTargetClass(targetClassClass);
           methodInvoker.setTargetObject(targetObject);
           methodInvoker.setTargetMethod(targetMethod);
           methodInvoker.setStaticMethod(staticMethod);
           methodInvoker.setArguments(arguments);
           methodInvoker.prepare();
           logger.info(</span>"Invoking: "+<span style="color: #000000;">methodInvoker.getPreparedMethod().toGenericString());
           methodInvoker.invoke();
          

          }
          catch(Exception e)
          {
          throw new JobExecutionException(e);
          }
          finally
          {
          logger.debug(
          "end");
          }
          }
          }

      public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob
      {
      // No additional functionality; just needs to implement StatefulJob.
      }

      public Object[] getArguments()
      {
      return arguments;
      }

      public void setArguments(Object[] arguments)
      {
      this.arguments = arguments;
      }

      public String getStaticMethod()
      {
      return staticMethod;
      }

      public void setStaticMethod(String staticMethod)
      {
      this.staticMethod = staticMethod;
      }

      public void setTargetObject(Object targetObject)
      {
      this.targetObject = targetObject;
      }
      }

View Code

web.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringQuartzCluster</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-quartz.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>60</session-timeout>
  </session-config>
</web-app>
复制代码

applicationContext-quartz.xml

正常情况应该从applicationContext.xml的总配置文件中import该applicationContext-quartz.xml. 由于小项目,直接作为总配置文件

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
    default-lazy-init="false" default-autowire="byName">
<span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> ClusterJob </span><span style="color: #008000;">--&gt;</span>
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="clusterJob"</span><span style="color: #ff0000;"> class</span><span style="color: #0000ff;">="king.quartz.job.ClusterJob"</span> <span style="color: #0000ff;">/&gt;</span>

<span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> ClusterJobDetail </span><span style="color: #008000;">--&gt;</span>
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="clusterJobDetail"</span><span style="color: #ff0000;">
    class</span><span style="color: #0000ff;">="king.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> concurrent(并发) : false表示等上一个任务执行完后再开启新的任务 </span><span style="color: #008000;">--&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="concurrent"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="false"</span> <span style="color: #0000ff;">/&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="shouldRecover"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="true"</span> <span style="color: #0000ff;">/&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="targetObject"</span><span style="color: #ff0000;"> ref</span><span style="color: #0000ff;">="clusterJob"</span> <span style="color: #0000ff;">/&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="targetMethod"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="execute"</span> <span style="color: #0000ff;">/&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span>

<span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> ClusterCronTriggerBean </span><span style="color: #008000;">--&gt;</span>
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="clusterCronTriggerBean"</span><span style="color: #ff0000;"> class</span><span style="color: #0000ff;">="org.springframework.scheduling.quartz.CronTriggerBean"</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="jobDetail"</span><span style="color: #ff0000;"> ref</span><span style="color: #0000ff;">="clusterJobDetail"</span> <span style="color: #0000ff;">/&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="cronExpression"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="0/7 * * * * ? *"</span> <span style="color: #0000ff;">/&gt;</span> <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 每10秒触发一次 </span><span style="color: #008000;">--&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span>

<span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> ClusterSchedulerFactoryBean </span><span style="color: #008000;">--&gt;</span>
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">bean </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="clusterSchedulerFactoryBean"</span><span style="color: #ff0000;">
    class</span><span style="color: #0000ff;">="org.springframework.scheduling.quartz.SchedulerFactoryBean"</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 指定quartz配置文件 </span><span style="color: #008000;">--&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="configLocation"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="classpath:quartz.properties"</span> <span style="color: #0000ff;">/&gt;</span>
     <span style="color: #008000;">&lt;!--</span><span style="color: #008000;">必QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动,单位秒 </span><span style="color: #008000;">--&gt;</span>    
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="startupDelay"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="1"</span> <span style="color: #0000ff;">/&gt;</span>    
    <span style="color: #008000;">&lt;!--</span><span style="color: #008000;"> 设置自动启动 </span><span style="color: #008000;">--&gt;</span>    
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="autoStartup"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="true"</span> <span style="color: #0000ff;">/&gt;</span>  
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="applicationContextSchedulerContextKey"</span><span style="color: #ff0000;"> value</span><span style="color: #0000ff;">="applicationContextKey"</span> <span style="color: #0000ff;">/&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">property </span><span style="color: #ff0000;">name</span><span style="color: #0000ff;">="triggers"</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">list</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">ref </span><span style="color: #ff0000;">bean</span><span style="color: #0000ff;">="clusterCronTriggerBean"</span> <span style="color: #0000ff;">/&gt;</span> 
        <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">list</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">property</span><span style="color: #0000ff;">&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">bean</span><span style="color: #0000ff;">&gt;</span>

</beans>

复制代码

quartz.properties

该配置文件一定要放在classpath目录下,因为SchedulerFactoryBean直接引用了configuration配置,所以还要配置相关数据源,见尾部Configure DataSource,其它保持原样即可,当然如果要调优的话里面参数肯定可以........

复制代码
#============================================================================
# Configure Main Scheduler Properties  
#============================================================================
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

============================================================================

Configure ThreadPool

============================================================================

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount
= 10
org.quartz.threadPool.threadPriority
= 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread
= true

============================================================================

Configure JobStore

============================================================================

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass
= org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.misfireThreshold
= 60000
org.quartz.jobStore.useProperties
= false
org.quartz.jobStore.tablePrefix
= QRTZ_
org.quartz.jobStore.dataSource
= myDS

org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval
= 15000

============================================================================

Configure DataSource

============================================================================

org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.myDS.URL
= jdbc:oracle:thin:@192.168.1.214:1521:DWXDBDEV
org.quartz.dataSource.myDS.user
= bobo
org.quartz.dataSource.myDS.password
= 11
org.quartz.dataSource.myDS.maxConnections
= 10

org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver

org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@LonelyTear-PC:1521:ORACLEDB

org.quartz.dataSource.myDS.user = bobo

org.quartz.dataSource.myDS.password = b

org.quartz.dataSource.myDS.maxConnections = 10

复制代码

 Main方法入口

复制代码
package king.quartz.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
    </span><span style="color: #008000;">//</span><span style="color: #008000;">下面的非web形式直接加载可能会导致job同时加载并执行2次,原因不明(历史印象是用了log4j的日志会导致该情况)
    </span><span style="color: #008000;">//</span><span style="color: #008000;">只能做普通测试用,最好在Tomcat等容器中以web形式运行,且指明docBase路径</span>
    <span style="color: #0000ff;">new</span> ClassPathXmlApplicationContext("applicationContext-quartz.xml"<span style="color: #000000;">);
}

}

复制代码

 当我们启动2次Main方法,最终只有一个Main会运行,如下:

复制代码
job = 65 is running 1 2 3 4 5 
job = 66 is running 1 2 3 4 5 
job = 67 is running 1 2 3 4 5 
job = 68 is running 1 2 3 4 5 
job = 69 is running 1 2 3 4 5 
job = 70 is running 1 2 3 4 5 
job = 71 is running 1 2 3 4 5 
复制代码

 sql

特别注意下面的sql版本要和squartz完全一致,在解压后的quartzquartz-1.8.6docsdbTables文件夹在都有

tables_mysql_innodb.sql

mysql对应数据表,拉下来全导入执行一遍

#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

DROP TABLE IF EXISTS QRTZ_JOB_LISTENERS;
DROP TABLE IF EXISTS QRTZ_TRIGGER_LISTENERS;
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS(
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_VOLATILE VARCHAR(1) NOT NULL,
IS_STATEFUL VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (JOB_NAME,JOB_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_JOB_LISTENERS (
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
JOB_LISTENER VARCHAR(200) NOT NULL,
PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),
INDEX (JOB_NAME, JOB_GROUP),
FOREIGN KEY (JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
IS_VOLATILE VARCHAR(1) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
INDEX (JOB_NAME, JOB_GROUP),
FOREIGN KEY (JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_TRIGGER_LISTENERS (
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
TRIGGER_LISTENER VARCHAR(200) NOT NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),
INDEX (TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (CALENDAR_NAME))
TYPE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (TRIGGER_GROUP))
TYPE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
IS_VOLATILE VARCHAR(1) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_STATEFUL VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (ENTRY_ID))
TYPE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (INSTANCE_NAME))
TYPE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (LOCK_NAME))
TYPE=InnoDB;

INSERT INTO QRTZ_LOCKS values('TRIGGER_ACCESS');
INSERT INTO QRTZ_LOCKS values(
'JOB_ACCESS');
INSERT INTO QRTZ_LOCKS values(
'CALENDAR_ACCESS');
INSERT INTO QRTZ_LOCKS values(
'STATE_ACCESS');
INSERT INTO QRTZ_LOCKS values(
'MISFIRE_ACCESS');
commit;

View Code

 tables_oracle.sql

oracle对应数据表,拉下来全导入执行一遍

--
-- A hint submitted by a user: Oracle DB MUST be created as "shared" and the 
-- job_queue_processes parameter  must be greater than 2, otherwise a DB lock 
-- will happen.   However, these settings are pretty much standard after any
-- Oracle install, so most users need not worry about this.
--
-- Many other users (including the primary author of Quartz) have had success
-- runing in dedicated mode, so only consider the above as a hint ;-)
--

delete from qrtz_job_listeners;
delete from qrtz_trigger_listeners;
delete from qrtz_fired_triggers;
delete from qrtz_simple_triggers;
delete from qrtz_cron_triggers;
delete from qrtz_blob_triggers;
delete from qrtz_triggers;
delete from qrtz_job_details;
delete from qrtz_calendars;
delete from qrtz_paused_trigger_grps;
delete from qrtz_locks;
delete from qrtz_scheduler_state;

drop table qrtz_calendars;
drop table qrtz_fired_triggers;
drop table qrtz_trigger_listeners;
drop table qrtz_blob_triggers;
drop table qrtz_cron_triggers;
drop table qrtz_simple_triggers;
drop table qrtz_triggers;
drop table qrtz_job_listeners;
drop table qrtz_job_details;
drop table qrtz_paused_trigger_grps;
drop table qrtz_locks;
drop table qrtz_scheduler_state;

CREATE TABLE qrtz_job_details
(
JOB_NAME
VARCHAR2(200) NOT NULL,
JOB_GROUP
VARCHAR2(200) NOT NULL,
DESCRIPTION
VARCHAR2(250) NULL,
JOB_CLASS_NAME
VARCHAR2(250) NOT NULL,
IS_DURABLE
VARCHAR2(1) NOT NULL,
IS_VOLATILE
VARCHAR2(1) NOT NULL,
IS_STATEFUL
VARCHAR2(1) NOT NULL,
REQUESTS_RECOVERY
VARCHAR2(1) NOT NULL,
JOB_DATA BLOB
NULL,
PRIMARY KEY (JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_job_listeners
(
JOB_NAME
VARCHAR2(200) NOT NULL,
JOB_GROUP
VARCHAR2(200) NOT NULL,
JOB_LISTENER
VARCHAR2(200) NOT NULL,
PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),
FOREIGN KEY (JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_triggers
(
TRIGGER_NAME
VARCHAR2(200) NOT NULL,
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
JOB_NAME
VARCHAR2(200) NOT NULL,
JOB_GROUP
VARCHAR2(200) NOT NULL,
IS_VOLATILE
VARCHAR2(1) NOT NULL,
DESCRIPTION
VARCHAR2(250) NULL,
NEXT_FIRE_TIME
NUMBER(13) NULL,
PREV_FIRE_TIME
NUMBER(13) NULL,
PRIORITY
NUMBER(13) NULL,
TRIGGER_STATE
VARCHAR2(16) NOT NULL,
TRIGGER_TYPE
VARCHAR2(8) NOT NULL,
START_TIME
NUMBER(13) NOT NULL,
END_TIME
NUMBER(13) NULL,
CALENDAR_NAME
VARCHAR2(200) NULL,
MISFIRE_INSTR
NUMBER(2) NULL,
JOB_DATA BLOB
NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
);
CREATE TABLE qrtz_simple_triggers
(
TRIGGER_NAME
VARCHAR2(200) NOT NULL,
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
REPEAT_COUNT
NUMBER(7) NOT NULL,
REPEAT_INTERVAL
NUMBER(12) NOT NULL,
TIMES_TRIGGERED
NUMBER(10) NOT NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_cron_triggers
(
TRIGGER_NAME
VARCHAR2(200) NOT NULL,
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
CRON_EXPRESSION
VARCHAR2(120) NOT NULL,
TIME_ZONE_ID
VARCHAR2(80),
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_blob_triggers
(
TRIGGER_NAME
VARCHAR2(200) NOT NULL,
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
BLOB_DATA BLOB
NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_trigger_listeners
(
TRIGGER_NAME
VARCHAR2(200) NOT NULL,
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
TRIGGER_LISTENER
VARCHAR2(200) NOT NULL,
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);
CREATE TABLE qrtz_calendars
(
CALENDAR_NAME
VARCHAR2(200) NOT NULL,
CALENDAR BLOB
NOT NULL,
PRIMARY KEY (CALENDAR_NAME)
);
CREATE TABLE qrtz_paused_trigger_grps
(
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
PRIMARY KEY (TRIGGER_GROUP)
);
CREATE TABLE qrtz_fired_triggers
(
ENTRY_ID
VARCHAR2(95) NOT NULL,
TRIGGER_NAME
VARCHAR2(200) NOT NULL,
TRIGGER_GROUP
VARCHAR2(200) NOT NULL,
IS_VOLATILE
VARCHAR2(1) NOT NULL,
INSTANCE_NAME
VARCHAR2(200) NOT NULL,
FIRED_TIME
NUMBER(13) NOT NULL,
PRIORITY
NUMBER(13) NOT NULL,
STATE
VARCHAR2(16) NOT NULL,
JOB_NAME
VARCHAR2(200) NULL,
JOB_GROUP
VARCHAR2(200) NULL,
IS_STATEFUL
VARCHAR2(1) NULL,
REQUESTS_RECOVERY
VARCHAR2(1) NULL,
PRIMARY KEY (ENTRY_ID)
);
CREATE TABLE qrtz_scheduler_state
(
INSTANCE_NAME
VARCHAR2(200) NOT NULL,
LAST_CHECKIN_TIME
NUMBER(13) NOT NULL,
CHECKIN_INTERVAL
NUMBER(13) NOT NULL,
PRIMARY KEY (INSTANCE_NAME)
);
CREATE TABLE qrtz_locks
(
LOCK_NAME
VARCHAR2(40) NOT NULL,
PRIMARY KEY (LOCK_NAME)
);
INSERT INTO qrtz_locks values('TRIGGER_ACCESS');
INSERT INTO qrtz_locks values('JOB_ACCESS');
INSERT INTO qrtz_locks values('CALENDAR_ACCESS');
INSERT INTO qrtz_locks values('STATE_ACCESS');
INSERT INTO qrtz_locks values('MISFIRE_ACCESS');
create index idx_qrtz_j_req_recovery on qrtz_job_details(REQUESTS_RECOVERY);
create index idx_qrtz_t_next_fire_time on qrtz_triggers(NEXT_FIRE_TIME);
create index idx_qrtz_t_state on qrtz_triggers(TRIGGER_STATE);
create index idx_qrtz_t_nft_st on qrtz_triggers(NEXT_FIRE_TIME,TRIGGER_STATE);
create index idx_qrtz_t_volatile on qrtz_triggers(IS_VOLATILE);
create index idx_qrtz_ft_trig_name on qrtz_fired_triggers(TRIGGER_NAME);
create index idx_qrtz_ft_trig_group on qrtz_fired_triggers(TRIGGER_GROUP);
create index idx_qrtz_ft_trig_nm_gp on qrtz_fired_triggers(TRIGGER_NAME,TRIGGER_GROUP);
create index idx_qrtz_ft_trig_volatile on qrtz_fired_triggers(IS_VOLATILE);
create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(INSTANCE_NAME);
create index idx_qrtz_ft_job_name on qrtz_fired_triggers(JOB_NAME);
create index idx_qrtz_ft_job_group on qrtz_fired_triggers(JOB_GROUP);
create index idx_qrtz_ft_job_stateful on qrtz_fired_triggers(IS_STATEFUL);
create index idx_qrtz_ft_job_req_recovery on qrtz_fired_triggers(REQUESTS_RECOVERY);

commit;

View Code

遇见异常

异常一 No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS.

  该异常产生原因是,我项目中用的是quartz-1.8.6的jar,但由于quartz版本差异过大,在不知情的情况下从官网down了最新的quartz-2.2.3且把2.2.3的dbTables文件夹下的tables_oracle.sql导了进来.所以最终和1.8.6的表结构不一致.

  就目前来看quartz-2.2.3的tables_oracle.sql比1.8.6的合理很多,1.8.6建表始化时还会建一些尴尬的外键关联,这是让我极不喜欢的一点.

异常如下,请点击展开

log4j:WARN No appenders could be found for logger (king.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'clusterSchedulerFactoryBean' defined in class path resource [applicationContext-quartz.xml]: Invocation of init method failed; nested exception is org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS [See nested exception: java.sql.SQLException: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1338)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:423)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at king.quartz.main.Main.main(Main.java:10)
Caused by: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS [See nested exception: java.sql.SQLException: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS]
    at org.quartz.impl.jdbcjobstore.StdRowLockSemaphore.executeSQL(StdRowLockSemaphore.java:109)
    at org.quartz.impl.jdbcjobstore.DBSemaphore.obtainLock(DBSemaphore.java:112)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3781)
    at org.quartz.impl.jdbcjobstore.JobStoreTX.executeInLock(JobStoreTX.java:90)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3712)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1095)
    at org.quartz.core.QuartzScheduler.addJob(QuartzScheduler.java:905)
    at org.quartz.impl.StdScheduler.addJob(StdScheduler.java:266)
    at org.springframework.scheduling.quartz.SchedulerAccessor.addJobToScheduler(SchedulerAccessor.java:317)
    at org.springframework.scheduling.quartz.SchedulerAccessor.addTriggerToScheduler(SchedulerAccessor.java:340)
    at org.springframework.scheduling.quartz.SchedulerAccessor.registerJobsAndTriggers(SchedulerAccessor.java:276)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:483)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
    ... 15 more
Caused by: java.sql.SQLException: No row exists in table QRTZ_LOCKS for lock named: TRIGGER_ACCESS
    at org.quartz.impl.jdbcjobstore.StdRowLockSemaphore.executeSQL(StdRowLockSemaphore.java:91)
    ... 28 more
View Code

 异常二 在xml配置中把定时任务的配置全删了,竟然还是执行了

这是spring的一个bug,所以需要去查询这三张表,它们储存了定时任务,把相关的定时任务删了即可

delete  from  QRTZ_CRON_TRIGGERS where TRIGGER_NAME = 'echannel.synchronizeJobTrigger';

delete from QRTZ_TRIGGERS where TRIGGER_NAME = 'echannel.synchronizeJobTrigger';

delete from QRTZ_JOB_DETAILS where job_name = 'echannel.synchronize.content';

 其它无关紧要的资料

SimpleTriggerBean

简易版Spring触发器,功能和org.springframework.scheduling.quartz.CronTriggerBean相似

<!-- ClusterSimpleTriggerBean 未使用,被注释了 -->
    <bean id="clusterSimpleTriggerBean" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
        <property name="startDelay" value="10000" />
        <property name="repeatInterval" value="10000" />
        <property name="jobDetail" ref="clusterJobDetail" />
    </bean>
View Code

参考

Spring(单机)定时任务的几种实现

Spring+quartz集群配置,Spring定时任务集群,quartz定时任务集群(强烈推荐)

quartz集群分布式(并发)部署解决方案-Spring

Spring中定时任务Quartz集群配置学习

Spring+Quartz 集群

spring结合Quartz的集群功能实现

spring+Quartz集群环境下配置详解 

本文原创,转载请说明出处 by 金墨痴 http://www.cnblogs.com/whatlonelytear/p/5823837.html 

作者:whatlonelytear
本文地址:https://www.cnblogs.com/whatlonelytear/p/5823837.html
欢迎转载,请在明显位置给出出处及链接。

查看全文
  • 相关阅读:
    【 socke】C# socket端口复用-多主机头绑定
    【网络】 NAT
    【socket】高级用法-ReceiveMessageFrom
    【socket】高级用法-异步
    C# GET 和 SET作用
    多态是什么意思?
    LINQ(LINQ to Entities)
    LINQ(隐式表达式、lambda 表达式)
    LINQ(LINQ to DataSet)
    C# 中的委托和事件
  • 原文地址:https://www.cnblogs.com/jpfss/p/9766746.html
  • Copyright © 2011-2022 走看看