任務調度的方式:Timer、ScheduledExecutorService、spring task、quartz、XXL-JOB、Elastic-Job


    1. 任務調度

定時任務調度:基於給定的時間點、給定的時間間隔、給定的執行次數自動執行的任務。

 

 

      1. Timer

介紹

Timer,簡單無門檻,一般也沒人用。

 

Timer位於java.util包下,其內部包含且僅包含一個后台線程(TimeThread)對多個業務任務(TimeTask)進行定時定頻率的調度。

 

https://pic4.zhimg.com/80/v2-4fb47836439037bec04cadbb07c548f3_hd.jpg

參數說明:

task:所要執行的任務,需要extends TimeTask override run()

time/firstTime:首次執行任務的時間

period:周期性執行Task的時間間隔,單位是毫秒

delay:執行task任務前的延時時間,單位是毫秒

很顯然,通過上述的描述,我們可以實現:

延遲多久后執行一次任務;指定時間執行一次任務;延遲一段時間,並周期性執行任務;指定時間,並周期性執行任務;

 

注意點

1:如果time/firstTime指定的時間,在當前時間之前,會發生什么呢?

在時間等於或者超過time/firstTime的時候,會執行task!也就是說,如果time/firstTime指定的時間在當前時間之前,就會立即得到執行。

 

2:schedule和scheduleAtFixedRate有什么區別?

scheduleAtFixedRate:每次執行時間為上一次任務開始起向后推一個period間隔,也就是說下次執行時間相對於上一次任務開始的時間點,因此執行時間不會延后,但是存在任務並發執行的問題。
schedule:每次執行時間為上一次任務結束后推一個period間隔,也就是說下次執行時間相對於上一次任務結束的時間點,因此執行時間會不斷延后。

 

3:如果執行task發生異常,是否會影響其他task的定時調度?

如果TimeTask拋出RuntimeException,那么Timer會停止所有任務的運行!

 

4:Timer的一些缺陷?

前面已經提及到Timer背后是一個單線程,因此Timer存在管理並發任務的缺陷:所有任務都是由同一個線程來調度,所有任務都是串行執行,意味着同一時間只能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之后的任務。
其次,Timer的一些調度方式還算比較簡單,無法適應實際項目中任務定時調度的復雜度。

 

5:停止和移除

cancel():終止Timer計時器,丟棄所有當前已安排的任務(TimeTask也存在cancel()方法,不過終止的是TimeTask)

purge():從計時器的任務隊列中移除已取消的任務,並返回個數

 

 

代碼示例

 

package schedule.timer;

 

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Timer;

import java.util.TimerTask;

 

/**

 * Timer背后是一個單線程,因此Timer存在管理並發任務的缺陷:所有任務都是由同一個線程來調度,所有任務都是串行執行,意味着同一時間只能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之后的任務。

 * 其次,Timer的一些調度方式還算比較簡單,無法適應實際項目中任務定時調度的復雜度。

 *

 *

 */

public class TimerTest {

    public static void main(String[] args) {

        Timer timer = new Timer();

       

        timer.schedule(new MyTask(), Calendar.getInstance().getTime(), 1000);

    }

}

 

class MyTask extends TimerTask{

    @Override

    public void run() {

        System.out.println("start at :"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(this.scheduledExecutionTime()));

       

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

 

      1. ScheduledExecutorService

介紹

JDK5之后便提供了基於線程池的定時任務調度:ScheduledExecutorService。

設計理念:每一個被調度的任務都會被線程池中的一個線程去執行,因此任務可以並發執行,而且相互之間不受影響。

 

示例

 

package schedule.scheduledExecutor;

 

import java.util.Date;

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

 

public class ScheduledExecutorServiceTest {

    public static void main(String[] args) {

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

        scheduledExecutorService.scheduleAtFixedRate(new ExecutorDemo(), 1000, 2000, TimeUnit.MILLISECONDS);

    }

}

 

class ExecutorDemo implements Runnable{

    @Override

    public void run() {

        System.out.println("執行"+new Date());

    }

}

 

 

      1. spring task

spring @Scheduled注解,一般集成於項目中,小任務很方便。

 

fixedRate和fixedDelay的區別

fixedRate :每隔多少毫秒執行一次該方法, 以上一次執行開始時間計算

fixedDelay:當一次方法執行完畢之后,延遲多少毫秒再執行該方法

 

 

例子1 注解方式

package schedule.springTask1;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/springTask1/spring-mvc.xml");

    }

}

 

 

package schedule.springTask1;

 

import java.util.Date;

 

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

 

/**

 * 基於注解的定時器

 */

@Component

public class SpringTaskAnnotation {

 

    // 定時計算。每一秒執行一次

    @Scheduled(cron = "0/1 * * * * *")

    public void show() {

        System.out.println(new Date() + " : Annotationis show run");

    }

 

    /**

     * 心跳更新。啟動時執行一次,之后每隔2秒執行一次

     */

    @Scheduled(fixedRate = 1000 * 2)

    public void print() {

        System.out.println(new Date() + " : Annotationis print run");

    }

 

    // 啟動加載緩存, 以上一次執行完為准

    @Scheduled(fixedDelay = 1 * 1000)

    public void init() {

        System.out.println(new Date() + ": Annotationis init run");

    }

}

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

 

    <!-- 定時器注解開關, 可以配置 scheduler參數 -->

    <task:annotation-driven />

    <!-- bean注解開關 -->

    <context:annotation-config />

    <!-- 自動掃描的包名 -->

    <context:component-scan base-package="schedule.springTask1" />

 

</beans>   

 

 

例子2 xml方式

package schedule.springTask2;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskXmlTest {

 

    public static void main(String[] args) throws BeansException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/springTask2/spring-mvc.xml");

    }

}

 

 

package schedule.springTask2;

 

import java.util.Date;

 

/**

 * 基於xml的定時器

 */

public class SpringTaskXml {

 

    public void show() {

        System.out.println(new Date() + " : XMl is show run");

    }

 

    public void print() {

        System.out.println(new Date() + " : XMl print run");

    }

}

 

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

 

    <bean id="task" class="schedule.springTask2.SpringTaskXml"></bean>

 

    <task:scheduled-tasks>

        <!-- 這里表示的是每隔五秒執行一次 -->

        <task:scheduled ref="task" method="show"

            cron="*/5 * * * * ?" />

        <task:scheduled ref="task" method="print"

            cron="*/10 * * * * ?" />

    </task:scheduled-tasks>

 

</beans>

 

 

 

 

 

 

 

      1. Spring Quartz

開源工具 Quartz,分布式集群開源工具,可以說是中小型公司必選,當然也視自身需求而定。

 

介紹

https://pic3.zhimg.com/80/v2-4b73fe0a6efe3484883b16a9bb347ab9_hd.jpg

 

兩種方式實現Spirng定時任務:

1.繼承自org.springframework.scheduling.quartz.QuartzJobBean
2.作業類即普通的java類,不需要繼承自任何基類。

 

定時器任務有兩種觸發器:

1.按照一定頻度調用任務,在Spring Quartz中對應的觸發器為:org.springframework.scheduling.quartz.SimpleTriggerBean
2.按照指定時間調用任務,在Spring Quartz中對應的調度器為:org.springframework.scheduling.quartz.CronTriggerBean

 

 

說明:
1、從代碼上來看,有XxxBuilder、XxxFactory,說明Quartz用到了Builder、Factory模式,還有非常易懂的鏈式編程風格。
2、Quartz有3個核心概念:調度器(Scheduler)、任務(Job&JobDetail)、觸發器(Trigger)。(一個任務可以被多個觸發器觸發,一個觸發器只能觸發一個任務)
3、注意當Scheduler調度Job時,實際上會通過反射newInstance一個新的Job實例(待調度完畢后銷毀掉),同時會把JobExecutionContext傳遞給Job的execute方法,Job實例通過JobExecutionContext訪問到Quartz運行時的環境以及Job本身的明細數據。
4、JobDataMap可以裝載任何可以序列化的數據,存取很方便。需要注意的是JobDetail和Trigger都可以各自關聯上JobDataMap。JobDataMap除了可以通過上述代碼獲取外,還可以在YourJob實現類中,添加相應setter方法獲取。
5、Trigger用來告訴Quartz調度程序什么時候執行,常用的觸發器有2種:SimpleTrigger(類似於Timer)、CronTrigger(類似於Linux的Crontab)。
6、實際上,Quartz在進行調度器初始化的時候,會加載quartz.properties文件進行一些屬性的設置,比如Quartz后台線程池的屬性(threadCount)、作業存儲設置等。它會先從工程中找,如果找不到那么就是用quartz.jar中的默認的quartz.properties文件。
7、Quartz存在監聽器的概念,比如任務執行前后、任務的添加等,可以方便實現任務的監控。

 

 

cronExpression配置說明

字段

允許值

允許的特殊字符

0-59

, - * /

0-59

, - * /

小時

0-23

, - * /

日期

1月31日

, - * ? / L W C

月份

1-12 或者 JAN-DEC

, - * /

星期

1-7 或者 SUN-SAT

, - * ? / L C #

年(可選)

留空, 1970-2099

, - * /

 

 

特殊字符說明:

字符

意義

*

表示所有值;

?

表示未說明的值,即不關心它為何值;

-

表示一個指定的范圍;

,

表示附加一個可能值;

/

符號前表示開始時間,符號后表示每次遞增的值;

L(“last”)

(“last”) “L” 用在day-of-month字段意思是

W(“weekday”)

只能用在day-of-month字段。用來描敘最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近這個月第15天的工作日”,即如果這個月第15天是周六,那么觸發器將會在這個月第14天即周五觸發;如果這個月第15天是周日,那么觸發器將會在這個月第16 天即周一觸發;如果這個月第15天是周二,那么就在觸發器這天觸發。注意一點:這個用法只會在當前月計算值,不會越過當前月。“W”字符僅能在day- of-month指明一天,不能是一個范圍或列表。也可以用“LW”來指定這個月的最后一個工作日。

#

只能用在day-of-week字段。用來指定這個月的第幾個周幾。例:在day-of-week字段用”6#3”指這個月第3個周五(6指周五,3指第3個)。如果指定的日期不存在,觸發器就不會觸發。

C

指和calendar聯系后計算過的值。例:在day-of-month 字段用“5C”指在這個月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在這周日或之后包括calendar的第一天。

 

 

Cron表達式教程

這里給出一些常用的示例:
0 15 10 ? * 每天10點15分觸發
0 15 10 ? 2017 2017年每天10點15分觸發
0 14 * ? 每天下午的 2點到2點59分每分觸發
0 0/5 14 ? 每天下午的 2點到2點59分(整點開始,每隔5分觸發)
0 0/5 14,18 ? 每天下午的 2點到2點59分、18點到18點59分(整點開始,每隔5分觸發)
0 0-5 14 ? 每天下午的 2點到2點05分每分觸發
0 15 10 ? * 6L 每月最后一周的星期五的10點15分觸發
0 15 10 ? * 6#3 每月的第三周的星期五開始觸發
我們可以通過一些Cron在線工具非常方便的生成,比如http://www.pppet.net/等。

 

 

CronTrigger

CronTriggers往往比SimpleTrigger更有用,如果您需要基於日歷的概念,而非SimpleTrigger完全指定的時間間隔,復發的發射工作的時間表。 CronTrigger,你可以指定觸發的時間表如“每星期五中午”,或“每個工作日9:30時”,甚至“每5分鍾一班9:00和10:00逢星期一上午,星期三星期五“。 即便如此,SimpleTrigger一樣,CronTrigger擁有的startTime指定的時間表時生效,指定的時間表時,應停止(可選)結束時間。

Cron表達式

cron的表達式被用來配置CronTrigger實例。 cron的表達式是字符串,實際上是由七子表達式,描述個別細節的時間表。這些子表達式是分開的空白,代表:

1. Seconds

2. Minutes

3. Hours

4. Day-of-Month

5. Month

6. Day-of-Week

7. Year (可選字段)

例 "0 0 12 ? * WED" 在每星期三下午12:00 執行,

個別子表達式可以包含范圍, 例如,在前面的例子里("WED")可以替換成 "MON-FRI", "MON, WED, FRI"甚至"MON-WED,SAT". “*” 代表整個時間段.

每一個字段都有一套可以指定有效值,如

Seconds (秒) :可以用數字0-59 表示,

Minutes(分) :可以用數字0-59 表示,

Hours(時) :可以用數字0-23表示,

Day-of-Month(天) :可以用數字1-31 中的任一一個值,但要注意一些特別的月份

Month(月) :可以用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示

Day-of-Week(每周):可以用數字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示

“/”:為特別單位,表示為“每”如“0/15”表示每隔15分鍾執行一次,“0”表示為從“0”分開始, “3/20”表示表示每隔20分鍾執行一次,“3”表示從第3分鍾開始執行

“?”:表示每月的某一天,或第周的某一天

“L”:用於每月,或每周,表示為每月的最后一天,或每個月的最后星期幾如“6L”表示“每月的最后一個星期五”

“W”:表示為最近工作日,如“15W”放在每月(day-of-month)字段上表示為“到本月15日最近的工作日”

““#”:是用來指定“的”每月第n個工作日,例 在每周(day-of-week)這個字段中內容為"6#3" or "FRI#3" 則表示“每月第三個星期五”

 

 

例子 1 cron簡單和復雜例子

package schedule.quartz1;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/quartz1/spring-mvc.xml");

    }

}

 

 

package schedule.quartz1;

 

public class SimpleJob {

    protected void execute() {

        System.out.println("簡單定時任務執行中…");

    }

}

 

 

package schedule.quartz1;

 

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.springframework.scheduling.quartz.QuartzJobBean;

 

public class ComplexJob extends QuartzJobBean {

 

    private int timeout;

 

    // 調度工廠實例化后,經過timeout時間開始執行調度

    public void setTimeout(int timeout) {

        this.timeout = timeout;

    }

 

    /**

     * 要調度的具體任務

     */

    @Override

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

        System.out.println("復雜定時任務執行中…");

    }

}

 

 

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

       

    <bean id="simpleJob" class="schedule.quartz1.SimpleJob"></bean>

   

    <!-- 使用 MethodInvokingJobDetailFactoryBean, 指定特定方法 -->

    <bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

        <property name="targetObject" ref="simpleJob" />

        <property name="targetMethod" value="execute" />

        <property name="concurrent" value="false" /><!-- 作業不並發調度 -->

    </bean>

   

    <!-- 簡單觸發器,使用 SimpleTriggerFactoryBean -->

    <bean id="simpleTrigger"  class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">

        <property name="jobDetail" ref="simpleJobDetail" />

        <property name="startDelay" value="1000" /><!-- 調度工廠實例化后,經過1000秒開始執行調度 -->

        <property name="repeatInterval" value="2000" /><!-- 2秒調度一次 -->

    </bean>

 

 

    <!-- 高級設置, 給作業傳遞數據 -->

    <bean id="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

        <property name="jobClass" value="schedule.quartz1.ComplexJob" />

 

        <property name="jobDataMap">

            <map>

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

            </map>

        </property>

 

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

    </bean>

 

    <!-- 計划觸發器,使用 CronTriggerFactoryBean -->

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

        <property name="jobDetail" ref="complexJobDetail" />

        <!--<property name="cronExpression" value="0/5 * * ? * SAT-SUN" /> -->

        <property name="cronExpression" value="0/5 * * ? * *" />

    </bean>

 

    <!-- 調度器 -->

    <bean

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

        <property name="triggers">

            <list>

                <ref bean="simpleTrigger" />

                <ref bean="cronTrigger" />

            </list>

        </property>

    </bean>

 

</beans>

 

 

例子 2 Scheduler start/stop manager配置方式

package schedule.quartz2;

 

import org.quartz.SchedulerException;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException, InterruptedException, SchedulerException {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/schedule/quartz2/spring-mvc.xml");

       

        QuartzManager quartzManager = applicationContext.getBean(QuartzManager.class);

        quartzManager.initJobs();

       

       

        quartzManager.addJob("xxxx", "yyyy", "zzzz", "gggg",MyJob.class, "0/3 * * ? * *");

       

       

        Thread.sleep(2000);

        System.out.println("--change to 2s--");

       

        quartzManager.trigger("xxxxcc", "yyyy", "zzzzx", "ggggx",MyJob2.class, "0/1 * * ? * *");

       

       

        quartzManager.modifyJobTime("xxxx", "yyyy", "zzzz", "gggg", "0/2 * * ? * *");

       

//      Thread.sleep(20000);

       

        System.out.println("--stop--");

       

//      quartzManager.shutdownJobs();

        Thread.sleep(20000);

    }

}

 

package schedule.quartz2;

 

import org.apache.commons.lang3.StringUtils;

import org.quartz.CronScheduleBuilder;

import org.quartz.CronTrigger;

import org.quartz.JobBuilder;

import org.quartz.JobDetail;

import org.quartz.JobKey;

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.Trigger;

import org.quartz.TriggerBuilder;

import org.quartz.TriggerKey;

import org.springframework.beans.factory.annotation.Autowired;

 

public class QuartzManager {

 

    private Scheduler scheduler;

   

    @Autowired

    private IJobService jobService;

   

    //初始化所有的jobs

    public void initJobs() {

        Long count = jobService.listQuartzEntity(null);

       

        if(0!=count) {

            System.out.println(".....");

        }

    }

 

    /**

     * @Description: 添加一個定時任務

     *

     * @param jobName          任務名

     * @param jobGroupName     任務組名

     * @param triggerName      觸發器名

     * @param triggerGroupName 觸發器組名

     * @param jobClass         任務

     * @param cron             時間設置,參考quartz說明文檔

     */

    @SuppressWarnings({ "unchecked", "rawtypes" })

    public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) {

        try {

            // 任務名,任務組,任務執行類

            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();

 

            // 觸發器

            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

            // 觸發器名,觸發器組

            triggerBuilder.withIdentity(triggerName, triggerGroupName);

            triggerBuilder.startNow();

            // 觸發器時間設定

            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));

            // 創建Trigger對象

            CronTrigger trigger = (CronTrigger) triggerBuilder.build();

 

            // 調度容器設置JobDetailTrigger

            scheduler.scheduleJob(jobDetail, trigger);

 

            // 啟動

            if (!scheduler.isShutdown()) {

                scheduler.start();

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

   

    /**

     * 觸發任務

     * @throws SchedulerException

     */

    public void trigger(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {

        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

       

        //沒有對應的jobtrigger

        if(null==jobDetail && null==scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {

            addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);

        }else {

            JobKey key = new JobKey(jobName, jobGroupName);

            scheduler.triggerJob(key);

        }

    }

   

    /**

     * 停止任務

     * @throws SchedulerException

     */

    public void pauseJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {

        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

       

        //沒有對應的jobtrigger

        if(null!=jobDetail && null!=scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {

            JobKey key = new JobKey(jobName, jobGroupName);

            scheduler.pauseJob(key);

        }

    }

   

    /**

     * 恢復任務

     * @throws SchedulerException

     */

    public void resumeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {

        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

       

        //沒有對應的jobtrigger

        if(null!=jobDetail && null!=scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {

            JobKey key = new JobKey(jobName, jobGroupName);

            scheduler.resumeJob(key);

        }

    }  

 

    /**

     * @Description: 修改一個任務的觸發時間

     *

     * @param jobName

     * @param jobGroupName

     * @param triggerName      觸發器名

     * @param triggerGroupName 觸發器組名

     * @param cron             時間設置,參考quartz說明文檔

     */

    public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {

        try {

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            if (trigger == null) {

                return;

            }

 

            String oldTime = trigger.getCronExpression();

            if (!oldTime.equalsIgnoreCase(cron)) {

                /** 方式一 :調用 rescheduleJob 開始 */

                // 觸發器

                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

                // 觸發器名,觸發器組

                triggerBuilder.withIdentity(triggerName, triggerGroupName);

                triggerBuilder.startNow();

                // 觸發器時間設定

                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));

                // 創建Trigger對象

                trigger = (CronTrigger) triggerBuilder.build();

                // 方式一 :修改一個任務的觸發時間

                scheduler.rescheduleJob(triggerKey, trigger);

                /** 方式一 :調用 rescheduleJob 結束 */

 

                /** 方式二:先刪除,然后在創建一個新的Job */

                // JobDetail jobDetail =

                // scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

                // Class<? extends Job> jobClass = jobDetail.getJobClass();

                // removeJob(jobName, jobGroupName, triggerName,

                // triggerGroupName);

                // addJob(jobName, jobGroupName, triggerName, triggerGroupName,

                // jobClass, cron);

                /** 方式二 :先刪除,然后在創建一個新的Job */

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    /**

     * @Description: 移除一個任務

     *

     * @param jobName

     * @param jobGroupName

     * @param triggerName

     * @param triggerGroupName

     */

    public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {

        try {

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

 

            scheduler.pauseTrigger(triggerKey);// 停止觸發器

            scheduler.unscheduleJob(triggerKey);// 移除觸發器

            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 刪除任務

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    /**

     * @Description:啟動所有定時任務

     */

    public void startJobs() {

        try {

            scheduler.start();

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    /**

     * @Description:關閉所有定時任務

     */

    public void shutdownJobs() {

        try {

            if (!scheduler.isShutdown()) {

                scheduler.shutdown();

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    public Scheduler getScheduler() {

        return scheduler;

    }

 

    public void setScheduler(Scheduler scheduler) {

        this.scheduler = scheduler;

    }

 

    public String transCron(String time) {

        String seconds = StringUtils.substringAfterLast(time, ":");

        String minute = StringUtils.substringAfter(time, ":").substring(0, 2);

        String hour = StringUtils.substringAfter(time, " ").substring(0, 2);

        String day = StringUtils.substringAfterLast(time, "-").substring(0, 2);

        String month = StringUtils.substringAfter(time, "-").substring(0, 2);

        return seconds + " " + minute + " " + hour + " " + day + " " + month + " ?";

    }

}

 

package schedule.quartz2;

 

import lombok.Data;

 

@Data

public class QuartzEntity{

    private String jobName;//任務名稱

    private String jobGroup;//任務分組

    private String description;//任務描述

    private String jobClassName;//執行類

    private String cronExpression;//執行時間

    private String triggerName;//執行時間

    private String triggerState;//任務狀態

   

    private String oldJobName;//任務名稱 用於修改

    private String oldJobGroup;//任務分組 用於修改

}

 

 

package schedule.quartz2;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class MyJob implements Job {

 

    private Logger logger = LoggerFactory.getLogger(MyJob.class);

 

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        try {

            logger.info("test】動態定時調度測試");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

package schedule.quartz2;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class MyJob2 implements Job {

 

    private Logger logger = LoggerFactory.getLogger(MyJob2.class);

 

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        try {

            logger.info("test】動態定時調度測試 ------------2");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

package schedule.quartz2;

 

import java.util.List;

 

public interface IJobService {

    List<QuartzEntity> listQuartzEntity(QuartzEntity quartz,Integer pageNo,Integer pageSize);

    Long listQuartzEntity(QuartzEntity quartz); 

}

 

 

package schedule.quartz2;

import java.util.List;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.util.StringUtils;

 

@Service("jobService")

public class JobServiceImpl implements IJobService {

 

    @Autowired

    private DynamicQuery dynamicQuery;

 

    @Override

    public List<QuartzEntity> listQuartzEntity(QuartzEntity quartz,

            Integer pageNo, Integer pageSize) {

        StringBuffer nativeSql = new StringBuffer();

        nativeSql.append("SELECT job.JOB_NAME as jobName,job.JOB_GROUP as jobGroup,job.DESCRIPTION as description,job.JOB_CLASS_NAME as jobClassName,");

        nativeSql.append("cron.CRON_EXPRESSION as cronExpression,tri.TRIGGER_NAME as triggerName,tri.TRIGGER_STATE as triggerState,");

        nativeSql.append("job.JOB_NAME as oldJobName,job.JOB_GROUP as oldJobGroup ");

        nativeSql.append("FROM qrtz_job_details AS job LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME ");

        nativeSql.append("LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME ");

        nativeSql.append("WHERE tri.TRIGGER_TYPE = 'CRON'");

        Object[] params = new  Object[]{};

        if(!StringUtils.isEmpty(quartz.getJobName())){//加入JobName搜索其他條件自行實現

            nativeSql.append(" AND job.JOB_NAME = ?");

            params = new Object[]{quartz.getJobName()};

        }

        return null;

    }

 

    @Override

    public Long listQuartzEntity(QuartzEntity quartz) {

        return 0L;

       

//      StringBuffer nativeSql = new StringBuffer();

//      nativeSql.append("SELECT COUNT(*)");

//      nativeSql.append("FROM qrtz_job_details AS job LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME ");

//      nativeSql.append("LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME ");

//      nativeSql.append("WHERE tri.TRIGGER_TYPE = 'CRON'");

//      return dynamicQuery.nativeQueryCount(nativeSql.toString(), new Object[]{});

    }

}

 

 

package schedule.quartz2;

import java.util.List;

/**

 * 擴展SpringDataJpa, 支持動態jpql/nativesql查詢並支持分頁查詢

 * 使用方法:注入ServiceImpl

 */

public interface DynamicQuery {

   

   

}

 

 

package schedule.quartz2;

import org.springframework.stereotype.Repository;

 

@Repository

public class DynamicQueryImpl implements DynamicQuery {

   

   

}

 

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <context:component-scan base-package="schedule.quartz2"></context:component-scan>

 

    <bean id="startQuartz" lazy-init="true" autowire="no"

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

        <property name="triggers">

            <list>

                <!--

                <ref bean="cronTrigger" />

                 -->

            </list>

        </property>

    </bean>

 

    <bean id="quartzManager" class="schedule.quartz2.QuartzManager" lazy-init="false" init-method="startJobs">

        <!--這個對象一定要注入,這樣類才能進行管理,還有在類型要用get set方法,不然會報錯。 -->

        <property name="scheduler" ref="startQuartz" />

    </bean>

 

 

    <!-- 高級設置, 給作業傳遞數據 -->

    <bean id="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

        <property name="jobClass" value="schedule.quartz2.MyJob" />

 

        <property name="jobDataMap">

            <map>

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

            </map>

        </property>

 

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

    </bean>

 

    <!-- 計划觸發器,使用 CronTriggerFactoryBean -->

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

        <property name="jobDetail" ref="complexJobDetail" />

        <!--<property name="cronExpression" value="0/5 * * ? * SAT-SUN" /> -->

        <property name="cronExpression" value="0/5 * * ? * *" />

    </bean>

</beans>

 

例子 3 Scheduler start/stop java方式

package schedule.quartz3;

 

import org.quartz.JobBuilder;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SimpleScheduleBuilder;

import org.quartz.Trigger;

import org.quartz.TriggerBuilder;

import org.quartz.impl.StdSchedulerFactory;

 

public class QuartzTest {

    public static void main(String[] args) throws Exception {

        // 從調度程序工廠獲取一個調度程序的實例

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

 

        // 顯示調度程序的名稱(這里會展示我們在quartz.properties文件中的名稱)

        System.out.println("scheduleName = " + scheduler.getSchedulerName());

 

        /**

         * 重要: 定義一個job,並綁定到我們自定義的HelloJobclass對象

         * 這里並不會馬上創建一個HelloJob實例,實例創建是在scheduler安排任務觸發執行時創建的 這種機制也為后面使用Spring集成提供了便利

         */

        JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();

 

        // 聲明一個觸發器,現在就執行(schedule.start()方法開始調用的時候執行);並且每間隔2秒就執行一次

        // 觸發器

        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

        // 觸發器名,觸發器組

        triggerBuilder.withIdentity("trigger1", "group1");

        triggerBuilder.startNow();

        // 觸發器時間設定

//      triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * ? * *"));

    triggerBuilder.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever());

       

       

        Trigger trigger = triggerBuilder.build();

 

        // 告訴quartz使用定義的觸發器trigger安排執行任務job

        scheduler.scheduleJob(job, trigger);

 

        // 啟動任務調度程序,內部機制是線程的啟動

        scheduler.start();

 

        // 關閉任務調度程序,如果不關閉,調度程序schedule會一直運行着

        // scheduler.shutdown();

 

    }

}

 

 

package schedule.quartz3;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class MyJob implements Job {

 

    private Logger logger = LoggerFactory.getLogger(MyJob.class);

 

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        try {

            logger.info("test】動態定時調度測試");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

 

 

 

 

 

 

 

 

 

 

 

      1. XXL-JOB

 

分布式任務 XXL-JOB,是一個輕量級分布式任務調度框架,支持通過 Web 頁面對任務進行 CRUD 操作,支持動態修改任務狀態、暫停/恢復任務,以及終止運行中任務,支持在線配置調度任務入參和在線查看調度結果。

 

https://github.com/xuxueli/xxl-job

 

https://www.cnblogs.com/xuxueli/p/5021979.html

 

 

 

 

 

 

 

 

 

 

 

      1. Elastic-Job

分布式任務 Elastic-Job,是一個分布式調度解決方案,由兩個相互獨立的子項目 Elastic-Job-Lite 和 Elastic-Job-Cloud 組成。定位為輕量級無中心化解決方案,使用 jar 包的形式提供分布式任務的協調服務。支持分布式調度協調、彈性擴容縮容、失效轉移、錯過執行作業重觸發、並行調度、自診。

 

 

https://gitee.com/52itstyle/spring-boot-quartz


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM