1. 程式人生 > >【Quatz】Quatz實踐

【Quatz】Quatz實踐

前言

定時任務是經常遇到的,主要有單機定時和分散式定時。今天簡單總結一下寫的幾個小例子。之後再總結quatz的原理,原始碼,其他的定時排程器等。

核心元件

  • scheduler排程器
  • job 任務
  • trigger 觸發器
    這裡寫圖片描述

這裡寫圖片描述

使用單純的quatz包

  • pom
  <!--引入quartz-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId
>
<version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency
>
  • job類
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("Hello Time " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss", Locale.CHINESE)));
    }

}
  • 主要類
public class LessonTwo {
    public void run() throws  Exception{
        //建立排程器
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        //建立job
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("job2","group2").build();
        //建立trigger
//        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger2","group2").startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.MINUTE)).build();
        SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity("trigger2","group2").startAt(DateBuilder.nextGivenSecondDate(null,1)).withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(3)).build();

        //組裝排程器
        scheduler.scheduleJob(jobDetail,trigger);
        //列印
        System.out.println("schduler says " + jobDetail.getKey() + " will run at" + trigger.getStartTime() + "interval " + trigger.getRepeatInterval()+ "repeatCount : "+ trigger.getRepeatCount());
        //排程器開始
        scheduler.start();
        //排程器關閉
        scheduler.shutdown();
    }
}

trigger中可以定義什麼時候觸發,隔多長時間觸發一次,一共觸發幾次。

使用quatz和spring繼承的jar包

  • pom
 <!--quartz需要 該tx jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

單機版

  • 配置檔案
 <!--有基類的定時任務配置-->
    <!--job配置-->
    <bean id="quartzJob1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.hlp.myProject.job.quartzSimple.QuartzJob1"/>
        <property name="jobDataAsMap">
            <map>
                <entry key="timeout" value="0"/>
            </map>
        </property>
    </bean>
    <!--trigger配置-->
    <bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="quartzJob1"/>
        <property name="cronExpression" value="0 0/1 * * * ? *"/>
    </bean>
    <!--*******************************-->
    <bean id="quartzJob2" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject">
            <bean class="com.hlp.myProject.job.quartzSimple.QuartzJob2"/>
        </property>
        <property name="targetMethod" value="run"/>
        <property name="concurrent" value="false"/>
    </bean>
    <bean id="cronTrigger2" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="quartzJob2"/>
        <property name="cronExpression" value="0 0/1 * * * ? *"/>
    </bean>

    <!--單機版 排程器配置-->
    <bean id="stdScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="cronTrigger1"/>
                <ref bean="cronTrigger2"/>
            </list>
        </property>
    </bean>

配置檔案一般是定義好一個元件1,在定義另一元件2時會引用元件1,從而繫結關係。一般這種引用用的是而不是。
- job1

/**
 * 基於基類的單機版定時任務
 * Created by Summer on 2018-06-30.
 */
public class QuartzJob1 extends QuartzJobBean {
    private Integer timeout;
    public void setTimeout(Integer timeout){
        this.timeout = timeout;
    }

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        LocalDateTime localDateTime = LocalDateTime.now();
        String s = localDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss", Locale.CHINESE));
        System.out.println(s + "基於基類QuartzJobBean的任務類開始執行了");
    }
}
  • job2
/**
 * 沒有基類的單機版定時任務
 * Created by Summer on 2018-06-30.
 */
public class QuartzJob2 {
    public void run(){
        LocalDateTime local = LocalDateTime.now();
        String s = local.format(DateTimeFormatter.ofPattern("HH:mm:ss", Locale.CHINESE));
        System.out.println(s + "沒有基類的單機版定時任務開始執行");
    }

}

叢集版

  • 配置檔案
 <!--叢集版配置資料來源-->
        <bean id="dataSourceQuartz" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="${db.url_quartz}"/>
            <property name="username" value="${db.username}"/>
            <property name="driverClassName" value="${db.driver}"/>
            <property name="password" value="${db.password}"/>
            <property name="maxActive" value="${db.maxActive}"/>
            <property name="initialSize" value="${db.initialSize}"/>
            <property name="maxWait" value="${db.maxWait}"/>
            <property name="minIdle" value="${db.minIdle}"/>
            <property name="timeBetweenConnectErrorMillis" value="${db.timeBetweenEvictionRunsMillis}"/>
            <property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"/>
            <property name="validationQuery" value="${db.validationQuery}"/>
            <property name="testWhileIdle" value="${db.testWhileIdle}"/>
            <property name="testOnBorrow" value="${db.testOnBorrow}"/>
            <property name="testOnReturn" value="${db.testOnReturn}"/>
            <property name="poolPreparedStatements" value="${db.poolPreparedStatements}"/>
            <property name="maxOpenPreparedStatements" value="${db.maxOpenPreparedStatements}"/>
            <property name="filters" value="stat,config"/>
            <property name="defaultAutoCommit" value="false"/>
        </bean>

        <!--執行緒池任務執行器-->
        <bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
            <property name="corePoolSize" value="10"/>
            <property name="maxPoolSize" value="100"/>
            <property name="queueCapacity" value="500"/>
        </bean>

        <!--把job和配置檔案綁一起-->
        <bean id="quartzJob1" class="com.hlp.myProject.job.quartzSimple.QuartzJob1" />

        <bean id="testMethod2" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
            <property name="durability" value="true"/>
            <property name="requestsRecovery" value="true"/>
            <property name="jobClass" value="com.hlp.myProject.job.quartzCluster.MyDetailQuartzJobBean"/>
            <property name="jobDataAsMap">
                <map>
                    <entry key="targetObject" value="quartzJob1"/>
                    <entry key="targetMethod" value="executeInternal"/>
                </map>
            </property>
        </bean>

        <!--把剛才綁好和trigger綁上-->
        <bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail" ref="testMethod2"/>
            <property name="cronExpression" value="0/5 * * * * ? *"/>
        </bean>

        <!--把資料來源和trigger綁上-->
        <bean id="stdScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="dataSource" ref="dataSourceQuartz"/>
            <!--延時啟動-->
            <property name="startupDelay" value="30"/>
            <property name="applicationContextSchedulerContextKey" value="applicationKey"/>
            <property name="configLocation" value="classpath:conf/quartz.properties"/>
            <property name="overwriteExistingJobs" value="true"/>
            <property name="autoStartup" value="true"/>
            <property name="triggers">
                <list>
                    <ref bean="trigger1"/>
                </list>
            </property>
        </bean>
  • quatz.properties
org.quartz.scheduler.instanceName=TestScheduler1
db.driver=com.mysql.cj.jdbc.Driver
org.quartz.scheduler.instanceId=AUTO
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=qrtz_
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered=true 
org.quartz.jobStore.clusterCheckinInterval=20000
#org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
#org.quartz.dataSource.myDS.URL = jdbc:mysql://192.168.22.141:3306/dmsd_quartz?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
#org.quartz.dataSource.myDS.user = root
#org.quartz.dataSource.myDS.password = root
#org.quartz.dataSource.myDS.maxConnections = 30
#org.quartz.jobStore.selectWithLockSQL = SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE
  • job

/**
 * quartz分散式叢集 工廠類
 * Created by Summer on 2018-06-30.
 */
/*
@PersistJobDataAfterExecution  新增到 Job 類後,表示 Quartz 將會在成功執行 execute() 方法後(沒有丟擲異常)更新 JobDetail 的 JobDataMap,下一次執行相同的任務(JobDetail)將會得到更新後的值,而不是原始的值。就像@DisallowConcurrentExecution 一樣,這個註釋基於 JobDetail 而不是 Job 類的例項。
@DisallowConcurrentExecution   不允許併發執行
 */
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Component
public class MyDetailQuartzJobBean extends QuartzJobBean implements ApplicationContextAware{
    private static final Logger logger = LoggerFactory.getLogger(MyDetailQuartzJobBean.class);


    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;
    }

    private String targetObject;
    private String targetMethod;

    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try{
            Object object = ctx.getBean(targetObject);
            Method m = object.getClass().getMethod(targetMethod);
            m.invoke(object);


        }catch (Exception e){
            e.printStackTrace();
            logger.error("定時器異常",e);

        }
    }

    public  void setTargetObject(String targetMethod){
        this.targetObject = targetObject;

    }

    public  void setTargetMethod(String targetMethod){
        this.targetMethod = targetMethod;
    }


}

quatz與spring整合的特點是應用了配置檔案,三大元件及關係都配置在了配置檔案中。類中需要寫的就是一個job.

執行的時候直接點選上面的執行就可以了,因為會執行配置檔案中的邏輯。之前還一直疑惑沒有main方法從哪裡執行呢。

小結

先定義一個任務,在定義一個觸發器並把任務繫結到觸發器上,然後再定義一個總的排程器,把觸發器,任務都綁上去。這就是簡單流程了。