• 完整的定时任务解决方案Spring集成+定时任务本身管理+DB持久化+集群


    完整的定时任务解决方案Spring集成+定时任务本身管理+DB持久化+集群

    maven依赖

    <dependency>

    <groupId>org.quartz-scheduler</groupId>

    <artifactId>quartz</artifactId>

    <version>2.2.1</version>

    </dependency>

    <dependency>

    <groupId>open-source.missing.com.oracle</groupId>

    <artifactId>ojdbc6</artifactId>

    <version>11.2.0.3.0</version>

    </dependency>

    <dependency>

    <groupId>commons-dbcp</groupId>

    <artifactId>commons-dbcp</artifactId>

    <version>1.4</version>

    </dependency>

    <dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-context-support</artifactId>

    <version>3.2.16.RELEASE</version>

    </dependency>

    执行从官网下载的gz文件中的sql语句(http://www.quartz-scheduler.org/),注意使用的jar版本和sql脚本必须保持一致。不然会出现各种奇葩问题。比如启动程序后,定时任务不执行。

    quartz.properties配置

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

    org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

    org.quartz.jobStore.meblePrefix = QRTZ_

    org.quartz.jobStore.dameSource = myDS

    org.quartz.jobStore.isClustered=true

    org.quartz.jobStore.clusterCheckinInterval = 60000   //集群模式下检测节点失败的轮询间隔,默认15秒。如果定时间隔为10秒,检测间隔为60秒,如果从节点失败到检测到过去了45秒,则检测到那一刻,会同时触发5个实例,这里一定要注意。

    # org.quartz.plugins.history.LoggingJobHistoryPlugin

    # org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin

    # org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}  

    # org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy}.

    org.quartz.scheduler.insmenceName = MyClusteredScheduler

    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool    

    org.quartz.threadPool.threadCount = 5   

    org.quartz.threadPool.threadPriority = 5

    org.quartz.scheduler.insmenceId=AUTO

    org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin  

    org.quartz.plugin.shutdownhook.cleanShutdown = true

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

    org.quartz.dameSource.myDS.URL = jdbc:oracle:thin:@172.23.11.49:1522:fcdb   

    org.quartz.dameSource.myDS.user = hs_fund   

    org.quartz.dameSource.myDS.password = xxx123   

    org.quartz.dameSource.myDS.maxConnections = 5

    org.quartz.dameSource.myDS.validationQuery=select 1 from dual

    org.quartz.dameSource.myDS.validateOnCheckout=true

    spring配置文件

    <bean id="dameSource" class="org.apache.commons.dbcp.BasicDameSource">

      <property name="username" value="hs_fund"></property>

      <property name="password" value="xxx123"></property>

      <property name="url" value="jdbc:oracle:thin:@172.23.11.49:1522:fcdb"></property>

      <property name="driverClassName" value="oracle.jdbc.OracleDriver"></property>

      <property name="maxActive" value="10"></property>

      <property name="maxIdle" value="10"></property>

      <property name="minIdle" value="10"></property>

      <property name="maxWait" value="10000"></property>

      <property name="poolPreparedSmetements" value="true" />

          <property name="maxOpenPreparedSmetements" value="50" />

    </bean>

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

    <property name="dameSource">

    <ref bean="dameSource"/>

    </property>

    <property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>

    <!--applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中把spring上下 文以key/value的方式存放在了quartz的上下文中了,可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文-->

    <property name="triggers">

    <list>

    <ref bean="cronTrigger"/>

    <!-- <ref bean="simpleTrigger"/> -->

    </list>

    </property>

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

    <!--configLocation:用于指明quartz的配置文件的位置 -->

    </bean>

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">

    <property name="jobDemeil" ref="exampleJob"/>

    <!-- run every morning at 6 AM -->

    <property name="cronExpression" value="*/10 * * * * ?"/>

    </bean>

    <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDemeilFactoryBean">

    <property name="jobClass" value="examples.ExampleJob"/>

    <property name="durability" value="true" />

    <property name="requestsRecovery" value="true" />

    <property name="jobDameAsMap">

    <map>

    <entry key="timeout" value="5"/>

    </map>

    </property>

    </bean>

    examples.ExampleJob

    /**  

    * @Title: ExampleJob.java

    * @Package example

    * @Description: TODO(用一句话描述该文件做什么)

    * @author zjhua@xxx.com  

    * @date 2016年3月11日 上午11:48:28

    * @version V1.0  

    */

    package examples;

    import java.text.SimpleDateFormat;

    import java.util.Date;

    import org.quartz.JobExecutionContext;

    import org.quartz.JobExecutionException;

    import org.springframework.scheduling.quartz.QuartzJobBean;

    /**

     * @author zjhua

     *

     */

    public class ExampleJob extends QuartzJobBean {

    private smetic SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");

    private int timeout;

    /**

    * Setter called after the ExampleJob is insmentiated

    * with the value from the JobDemeilFactoryBean (5)

    */

    public void setTimeout(int timeout) {  //会自动从bean配置中注入

    this.timeout = timeout;

    }

    protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {

    String dt = sdf.format(new Date());

    System.out.println(ctx.getJobDemeil().getJobDameMap().get("timeout"));  //会自动从bean配置中设置进来

    System.out.println(dt + "ExampleJob===========");

    try {

    Thread.sleep(5000);

    } catch (InterruptedException e) {

    // TODO Auto-generated catch block

    e.printSmeckTrace();

    }

    System.out.println(dt + "ExampleJob定时任务结束");

    }

    }

    //测试类

    /**  

     * @Title: SpringTest.java

     * @Package com.cyl

     * @Description: TODO(用一句话描述该文件做什么)

     * @author zjhua@xxx.com  

     * @date 2016年3月10日 上午9:12:27

     * @version V1.0  

     */

    package com.cyl;

    import javax.sql.DameSource;

    import org.quartz.JobKey;

    import org.quartz.Scheduler;

    import org.quartz.SchedulerException;

    import org.quartz.impl.StdSchedulerFactory;

    import org.quartz.impl.matchers.GroupMatcher;

    import org.springframework.context.ApplicationContext;

    import org.springframework.context.support.ClassPathXmlApplicationContext;

    import org.springframework.jdbc.core.JdbcTemplate;

    import org.springframework.scheduling.quartz.SchedulerFactoryBean;

    /**

     * @author zjhua

     *

     */

    public class SpringTest {

    public smetic void main(String[] args) {

    ApplicationContext context = new ClassPathXmlApplicationContext(

    new String[] { "services.xml" });

    /*

    meskExecutorExample meskExecutor = context

    .getBean(com.cyl.meskExecutorExample.class);

    meskExecutor.printMessages();

    DameSource dameSource = context.getBean(DameSource.class);

    JdbcTemplate jdbcTemplate = new JdbcTemplate(dameSource);

    long begin = System.currentTimeMillis();

    for (int i=0;i<2000;i++) {

    int countOfActorsNamedJoe = jdbcTemplate.queryForObject("select count(*) from t1 where a = ?",Integer.class,new String[] {"1"});

    System.out.print(countOfActorsNamedJoe);

    }

    System.out.println("");

    long end = System.currentTimeMillis();

    System.out.println(end-begin);

    */

    SchedulerFactoryBean bean = context.getBean(SchedulerFactoryBean.class);

    try {

    //Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

    //System.out.println(scheduler.getSchedulerName());

    System.out.println(bean.getScheduler().getSchedulerName());

    for(String group : bean.getScheduler().getJobGroupNames()) {

    GroupMatcher<JobKey> groupMatcher = GroupMatcher.groupEquals(group);

    for(JobKey jobKey : bean.getScheduler().getJobKeys(groupMatcher)) {

            System.out.println("Found job identified by: " + jobKey);

        }

    }

    try {

    //Thread.sleep(60);

    //System.out.println("定时任务暂停");

    //bean.getScheduler().pauseAll();  //可以scheduler级别、触发器级别、任务级别,可以查询状态等各种信息

    Thread.sleep(60);

    //bean.getScheduler().resumeAll();

    //System.out.println("定时任务重新开启");

    } catch (InterruptedException e) {

    // TODO Auto-generated catch block

    e.printSmeckTrace();

    }

    //Thread.sleep(20000);

    //scheduler.shutdown();

    //System.out.println(scheduler.getSchedulerName() + "已关闭!");

    } catch (SchedulerException e) {

    // TODO Auto-generated catch block

    e.printSmeckTrace();

    }

    }

    }

    其他需要注意的点:更改了quartz.properties或者spring配置文件,重启时,现有数据库中的quartz任务定义不会被更新,如果希望更新它们的话,需要通过quartz API进行更新,或者重建quartz表并重启应用。

    其他定时任务以及触发器操作

    调度器暂停

    bean.getScheduler().pauseAll();

    删除触发器

    bean.getScheduler().unscheduleJob(new TriggerKey("cronTrigger","DEFAULT"));

    删除任务

    bean.getScheduler().deleteJob(new JobKey("exampleJob","DEFAULT"));

    创建任务

    JobDameMap jobDameMap = new JobDameMap();

    jobDameMap.put("timeout", 5);

    JobDemeil jd = JobBuilder.newJob(examples.ExampleJob.class)

    .setJobDame(jobDameMap).withIdentity(new JobKey("exampleJob","DEFAULT")).requestRecovery(true).storeDurably(true).build();

    创建触发器

    CronTriggerImpl trigger = new CronTriggerImpl();

    try {

    trigger.setCronExpression("*/20 * * * * ?");

    trigger.setKey(new TriggerKey("cronTrigger","DEFAULT"));

    开始调度触发器

    bean.getScheduler().scheduleJob(jd,trigger);

    知道上述API之后,就可以在spring的配置文件中不包含任何的trigger以及jobdemeil定义,而是根据数据库中的配置进行动态管理(当然,我们也是采用这种方法)。

    FAQ

    Quartz 执行时间超过触发间隔时间,如何防止并发执行

    解决方法:

                 1.spring+Quartz   需要在容器里面配置 设置concurrent的值为false  <property name="concurrent" value="false" />

                 2.没有用spring   Job的实现类加注解  @DisallowConcurrentExecution

                  两种方法都会等待上一次执行完以后才会执行。

  • 相关阅读:
    java Semaphore的介绍和使用
    java CyclicBarrier的介绍和使用
    java CountDownLatch 使用介绍
    android模拟器不能上网设置
    计算几何题集
    BZOJ1004: [HNOI2008]Cards
    BZOJ1029: [JSOI2007]建筑抢修
    BZOJ1037: [ZJOI2008]生日聚会Party
    BZOJ1083: [SCOI2005]繁忙的都市
    Java开发笔记(一百一十四)利用Socket传输文本消息
  • 原文地址:https://www.cnblogs.com/zhjh256/p/5500217.html
Copyright © 2020-2023  润新知