SpringBoot定時任務(schedule、quartz)

原文連接:https://www.cnblogs.com/jing99/p/11546559.html
html


Scheduledjava


只適合處理簡單的計劃任務,不能處理分佈式計劃任務。優點:是spring框架提供的計劃任務,開發簡單,執行效率比較高。且在計劃任務數量太多的時候,可能出現阻塞,崩潰,延遲啓動等問題。
  mysql

Scheduled定時任務是spring3.0版本以後自帶的一個定時任務。其所屬Spring的資源包爲:spring-context-support。因此須要使用Scheduled定時任務機制時,須要在工程中依賴對應資源,具體以下:web



<dependency>
  <groupId>org.springframeworkgroupId>
  <artifactId>spring-context-supportartifactId>
dependency>


若是在spring應用中須要啓用Scheduled定時任務,則須要在啓動類上增長註解@EnableScheduling,表明啓用Scheduled定時任務機制。具體以下:
spring


@SpringBootApplication
@EnableScheduling
public class AppStarter {

  public static void main(String[] args) {
    SpringApplication.run(AppStarter.class, args);
  }
}


Scheduled定時任務的核心在於註解@Scheduled,這個註解的核心屬性是cron,表明定時任務的觸發計劃表達式。這個表達式的格式爲:sql


@Scheduled(cron="seconds minutes hours day month week")



數據庫


@Scheduled(cron="seconds minutes hours day month week year")


推薦使用第一種表達式形式,由於在不少其餘技術中都有不一樣的定時任務機制,其中用於設置觸發計劃的表達式都是第一種cron表達式。第二種表達式不能說是Spring Scheduled特有的,也是隻有少數技術支持的。編程


cron表達式中,每一個位置的約束以下:api



星號(*):可用在全部字段中,表示對應時間域的每個時刻,例如,*在分鐘字段時,表示「每分鐘」;微信


問號(?):該字符只在日期和星期字段中使用,它一般指定爲「無心義的值」,至關於佔位符;


減號(-):表達一個範圍,如在小時字段中使用「10-12」,則表示從10到12點,即10,11,12;


逗號(,):表達一個列表值,如在星期字段中使用「MON,WED,FRI」,則表示星期一,星期三和星期五;


斜槓(/):x/y表達一個等步長序列,x爲起始值,y爲增量步長值。如在秒數字段中使用0/15,則表示爲0,15,30和45秒,而5/15在分鐘字段中表示5,20,35,50,你也可使用*/y,它等同於0/y;

  

L:該字符只在日期和星期字段中使用,表明「Last」的意思,但它在兩個字段中意思不一樣。L在日期字段中,表示這個月份的最後一天,如一月的31號,非閏年二月的28號;若是L用在星期中,則表示星期六,等同於7。可是,若是L出如今星期字段裏,並且在前面有一個數值X,則表示「這個月的最後X天」,例如,6L表示該月的最後星期五;

  

W:該字符只能出如今日期字段裏,是對前導日期的修飾,表示離該日期最近的工做日。例如15W表示離該月15號最近的工做日,若是該月15號是星期六,則匹配14號星期五;若是15日是星期日,則匹配16號星期一;若是15號是星期二,那結果就是15號星期二。但必須注意關聯的匹配日期不可以跨月,如你指定1W,若是1號是星期六,結果匹配的是3號星期一,而非上個月最後的那天。W字符串只能指定單一日期,而不能指定日期範圍;


LW組合:在日期字段能夠組合使用LW,它的意思是當月的最後一個工做日;

  

井號(#):該字符只能在星期字段中使用,表示當月某個工做日。如6#3表示當月的第三個星期五(6表示星期五,#3表示當前的第三個),而4#5表示當月的第五個星期三,假設當月沒有第五個星期三,忽略不觸發;

  

C:該字符只在日期和星期字段中使用,表明「Calendar」的意思。它的意思是計劃所關聯的日期,若是日期沒有被關聯,則至關於日曆中全部日期。例如5C在日期字段中就至關於日曆5日之後的第一天。1C在星期字段中至關於星期往後的第一天。


Cron表達式對特殊字符的大小寫不敏感,對錶明星期的縮寫英文大小寫也不敏感。

  

計劃任務Scheduled是經過一個線程池實現的。是一個多線程的調度。SpringBoot會初始化一個線程池,線程池默認大小爲1,專門用於執行計劃任務。每一個計劃任務啓動的時候,都從線程池中獲取一個線程執行,若是發生異常,只是執行當前任務的線程發生異常,而不是計劃任務調度線程發生異常。若是當前定時任務還未執行完成,當相同的定時任務又進入到執行週期時,不會觸發新的定時任務。如:


@Scheduled(cron="* * * * * ?")
public void test1(){
    Random r = new Random();
    /*int i = r.nextInt(100);
    if(i % 3 == 0){
        throw new RuntimeException("error");
    }*/

    System.out.println(Thread.currentThread().getName() + " cron=* * * * * ? --- " + new Date());
    try{
        Thread.sleep(2000);
    }catch(Exception e){
        e.printStackTrace();
    }
}


如結果所示(每次的線程名稱一致,因爲前一個定時任務未執行完成,所以形成後一個任務的推遲,而不是1秒執行一次,而是3秒):


pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:20 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:23 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:26 CST 2019
pool-1-thread-1 cron=* * * * * ? --- Thu Sep 19 02:23:29 CST 2019


quartz


Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它能夠與J2EE與J2SE應用程序相結合也能夠單獨使用。Quartz能夠用來建立簡單或爲運行十個,百個,甚至是好幾萬個Jobs這樣複雜的程序。


Quartz是一個徹底由java編寫的開源做業調度框架。不要讓做業調度這個術語嚇着你。儘管Quartz框架整合了許多額外功能, 但就其簡易形式看,你會發現它易用得簡直讓人受不了!


在開發Quartz相關應用時,只要定義了Job(任務),Trigger(觸發器)和Scheduler(調度器),便可實現一個定時調度能力。其中Scheduler是Quartz中的核心,Scheduler負責管理Quartz應用運行時環境,Scheduler不是靠本身完成全部的工做,是根據Trigger的觸發標準,調用Job中的任務執行邏輯,來完成完整的定時任務調度。


Job - 定時任務內容是什麼。
Trigger - 在什麼時間上執行job。
Scheduler - 維護定時任務環境,並讓觸發器生效。


在SpringBoot中應用Quartz,須要依賴下述資源:



        
         <dependency>
             <groupId>org.springframework groupId>
             <artifactId>spring-context-support artifactId>
         dependency>
        
         <dependency>
             <groupId>org.quartz-scheduler groupId>
             <artifactId>quartz artifactId>
             <version>2.2.1 version>
            
             <exclusions>
                 <exclusion>
                     <artifactId>slf4j-api artifactId>
                     <groupId>org.slf4j groupId>
                 exclusion>
             exclusions>
         dependency>
        
         <dependency>
             <groupId>org.springframework groupId>
             <artifactId>spring-tx artifactId>
         dependency>
dependencies>


啓動器添加註解@EnableScheduling:


/**
 * @EnableScheduling 必要
 * 開啓定時任務機制。
 */

@SpringBootApplication
@EnableScheduling
public class AppStarter {

    public static void main(String[] args) {
        SpringApplication.run(AppStarter.class, args);
    }
}


定義JOB任務以及JOB任務調用的模擬業務對象:


public class SpringBootQuartzJobDemo implements Job {

    // 用於模擬任務中的業務對象。也多是數據訪問對象,或其餘類型的對象。
    @Autowired
    private CommonsUtil4Quartz commonsUtil4Quartz;
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("SpringBootQuartzJobDemo : " + new Date());
        this.commonsUtil4Quartz.testMethod();
    }

}


@Component
public class CommonsUtil4Quartz {

    public void testMethod(){
        System.out.println("CommonsUtil4Quartz testMethod run...");
    }
}


建立Trigger以及JobDetail對象,並用Schedule配置定時任務:


/**
 * 初始化類
 * Quartz環境初始化。
 *
 */

@Configuration
public class QuartzConfiguration {

    /**
     * 建立Job對象。在Spring環境中,建立一個類型的對象的時候,不少狀況下,都是經過FactoryBean來間接建立的。
     * 若是有多個Job對象,定義屢次方法。
     *
     * 在JobDetailFactoryBean類型中,用於建立JobDetail對象的方法,其底層使用的邏輯是:Class.newInstance()
     * 也就是說,JobDetail對象不是經過Spring容器管理的。
     * 由於Spring容器無論理JobDetail對象,那麼Job中須要自動裝配的屬性,就沒法實現自動狀態。如上JOB的第10行會報空指針異常。
     *
     * 解決方案是:將JobDetail加入到Spring容器中,讓Spring容器管理JobDetail對象。
     * 須要重寫Factory相關代碼。實現Spring容器管理JobDetail。
     * @return
     */

    @Bean
    public JobDetailFactoryBean initJobDetailFactoryBean(){
        JobDetailFactoryBean factoryBean =
                new JobDetailFactoryBean();
        // 提供job類型。
        factoryBean.setJobClass(SpringBootQuartzJobDemo.class);
        
        return factoryBean;
    }
    
    /**
     * 管理Trigger對象
     * CronTrigger - 就是Trigger的一個實現類型。其中用於定義週期時間的是CronSchedulerBuilder
     * 實際上,CronTrigger是用於管理一個Cron表達式的類型。
     * @param jobDetailFactoryBean - 上一個方法初始化的JobDetailFactoryBean
     * @return
     */

    @Bean(name="cronTriggerFactoryBean1")
    public CronTriggerFactoryBean initCronTriggerFactoryBean(
            )
{
        CronTriggerFactoryBean factoryBean =
                new CronTriggerFactoryBean();
        
        JobDetailFactoryBean jobDetailFactoryBean = this.initJobDetailFactoryBean();
        
        factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
        
        factoryBean.setCronExpression("0/3 * * * * ?");
        
        return factoryBean;
    }
    
    /**
     * 初始化Scheduler
     * @param cronTriggerFactoryBean - 上一個方法初始化的CronTriggerFactoryBean
     * @return
     */

    @Bean
    public SchedulerFactoryBean initSchedulerFactoryBean(
            CustomJobFactory customJobFactory,
            CronTriggerFactoryBean[] cronTriggerFactoryBean)
{
        SchedulerFactoryBean factoryBean =
                new SchedulerFactoryBean();
        CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
        for(int i = 0; i < cronTriggerFactoryBean.length; i++){
            triggers[i] = cronTriggerFactoryBean[i].getObject();
        }
        // 註冊觸發器,一個Scheduler能夠註冊若干觸發器。
        factoryBean.setTriggers(triggers);
        // 爲Scheduler設置JobDetail的工廠。能夠覆蓋掉SpringBoot提供的默認工廠,保證JobDetail中的自動裝配有效。
        factoryBean.setJobFactory(customJobFactory);
        
        return factoryBean;
    }
    
}


重寫JobFactory:


/**
 * 重寫的工廠對象。
 */

@Component
public class CustomJobFactory extends AdaptableJobFactory {

    /**
     * AutowireCapableBeanFactory : 簡單理解爲Spring容器,是Spring容器Context的一個Bean對象管理工程。
     * 能夠實現自動裝配邏輯,和對象建立邏輯。
     * 是SpringIoC容器的一個重要組成部件。
     */

    @Autowired
    private AutowireCapableBeanFactory autowireCapableBeanFactory;
    
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        // 經過父類型中的方法,建立JobDetail對象。
        Object obj = super.createJobInstance(bundle);
        // 將JobDetail對象加入到Spring容器中,讓Spring容器管理,並實現自動裝配邏輯。
        this.autowireCapableBeanFactory.autowireBean(obj);
        
        return obj;
    }

}


分佈式quartz配置


一、資源依賴配置:因爲分佈式的緣由,Quartz中提供分佈式處理的jar包以及數據庫及鏈接相關的依賴。



        
         <dependency>
             <groupId>org.springframework groupId>
             <artifactId>spring-context-support artifactId>
         dependency>
        
         <dependency>
             <groupId>org.quartz-scheduler groupId>
             <artifactId>quartz artifactId>
             <version>2.2.1 version>
             <exclusions>
                 <exclusion>
                     <artifactId>slf4j-api artifactId>
                     <groupId>org.slf4j groupId>
                 exclusion>
             exclusions>
         dependency>
        
         <dependency>
             <groupId>org.quartz-scheduler groupId>
             <artifactId>quartz-jobs artifactId>
             <version>2.2.1 version>
         dependency>
        
         <dependency>
             <groupId>org.springframework groupId>
             <artifactId>spring-tx artifactId>
         dependency>
        
         <dependency>
             <groupId>org.springframework.boot groupId>
             <artifactId>spring-boot-starter-web artifactId>
         dependency>
        
         <dependency>
             <groupId>org.springframework.boot groupId>
             <artifactId>spring-boot-starter-data-jpa artifactId>
         dependency>
        
         <dependency>
             <groupId>mysql groupId>
             <artifactId>mysql-connector-java artifactId>
         dependency>
        
         <dependency>
             <groupId>com.alibaba groupId>
             <artifactId>druid artifactId>
             <version>1.0.9 version>
         dependency>
dependencies>


二、提供數據庫相關配置:


spring.datasource.url=jdbc:mysql://localhost:3306/quartz?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=600000
spring.datasource.timeBetweenEvictionRunsMillis=600000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false

server.port=8080


三、提供Quartz配置信息,爲Quartz提供數據庫配置信息,如數據庫,表格的前綴之類的。


# 是否使用properties做爲數據存儲
org.quartz.jobStore.useProperties=false
# 數據庫中的表格命名前綴
org.quartz.jobStore.tablePrefix = QRTZ_
# 是不是一個集羣,是否是分佈式的任務
org.quartz.jobStore.isClustered = true
# 集羣檢查週期,單位毫秒。能夠自定義縮短期。當某一個節點宕機的時候,其餘節點等待多久後開始執行任務。
org.quartz.jobStore.clusterCheckinInterval = 5000
# 單位毫秒, 集羣中的節點退出後,再次檢查進入的時間間隔。
org.quartz.jobStore.misfireThreshold = 60000
# 事務隔離級別
org.quartz.jobStore.txIsolationLevelReadCommitted = true
# 存儲的事務管理類型
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 使用的Delegate類型
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 集羣的命名,一個集羣要有相同的命名。
org.quartz.scheduler.instanceName = ClusterQuartz
# 節點的命名,能夠自定義。AUTO表明自動生成。
org.quartz.scheduler.instanceId= AUTO
# rmi遠程協議是否發佈
org.quartz.scheduler.rmi.export = false
# rmi遠程協議代理是否建立
org.quartz.scheduler.rmi.proxy = false
# 是否使用用戶控制的事務環境觸發執行job。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false


四、初始化數據庫

  

建表語句能夠本身在官方網站中查找(Quartz-lib中),使用tables-mysql.sql建表。

  

五、定義JOB類

  

啓動器和普通quartz無差別,可是JOB自身定義有些許差別:


/**
 * 使用Spring提供的Quartz相關Job類型實現Job的定義。
 * 父類型QuartzJobBean中,提供了分佈式環境中任務的配置定義。
 * 保證分佈式環境中的任務是有效的。
 */

@PersistJobDataAfterExecution // 當job執行結束,持久化job信息到數據庫
@DisallowConcurrentExecution // 保證job的惟一性(單例)
public class SpringBootQuartzJobDemo extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("SpringBootQuartzJobDemo : " + new Date());
    }
}


六、QuartzConfiguration類型定義


@Configuration
public class QuartzConfiguration {

    @Autowired
    private DataSource dataSource;
    /**
     * 建立調度器, 能夠省略的。
     * @return
     * @throws Exception
     */

    @Bean
    public Scheduler scheduler() throws Exception {
        Scheduler scheduler = schedulerFactoryBean().getScheduler();
        scheduler.start();
        return scheduler;
    }

    /**
     * 建立調度器工廠bean對象。
     * @return
     * @throws IOException
     */

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();

        factory.setSchedulerName("Cluster_Scheduler");
        factory.setDataSource(dataSource);
        factory.setApplicationContextSchedulerContextKey("applicationContext");
        // 設置調度器中的線程池。
        factory.setTaskExecutor(schedulerThreadPool());
        // 設置觸發器
        factory.setTriggers(trigger().getObject());
        // 設置quartz的配置信息
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }

    /**
     * 讀取quartz.properties配置文件的方法。
     * @return
     * @throws IOException
     */

    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));

        // 在quartz.properties中的屬性被讀取並注入後再初始化對象
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    /**
     * 建立Job對象的方法。
     * @return
     */

    @Bean
    public JobDetailFactoryBean job() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();

        jobDetailFactoryBean.setJobClass(SpringBootQuartzJobDemo.class);
        // 是否持久化job內容
        jobDetailFactoryBean.setDurability(true);
        // 設置是否屢次請求嘗試任務。
        jobDetailFactoryBean.setRequestsRecovery(true);

        return jobDetailFactoryBean;
    }

    /**
     * 建立trigger factory bean對象。
     * @return
     */

    @Bean
    public CronTriggerFactoryBean trigger() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();

        cronTriggerFactoryBean.setJobDetail(job().getObject());
        cronTriggerFactoryBean.setCronExpression("0/2 * * * * ?");

        return cronTriggerFactoryBean;
    }

    /**
     * 建立一個調度器的線程池。
     * @return
     */

    @Bean
    public Executor schedulerThreadPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(15);
        executor.setMaxPoolSize(25);
        executor.setQueueCapacity(100);

        return executor;
    }
}


若JOB任務有定義調用業務等內容,也須要重寫JobFactory,如上述常規quartz,此處再也不贅述。

END


我是武哥,最後給你們 免費分享我寫的 10 萬字 Spring Boot 學習筆記(帶完整目錄)以及對應的源碼 。這是我以前在 CSDN 開的一門課,因此筆記很是詳細完整,我準備將資料分享出來給你們免費學習,相信你們看完必定會有所收穫( 下面有下載方式 )。


能夠看出,我當時備課很是詳細,目錄很是完整,讀者能夠手把手跟着筆記,結合源代碼來學習。如今免費分享出來,有須要的讀者能夠下載學習,就在我公衆號回覆:筆記,就行。



若有文章對你有幫助,

在看轉發是對我最大的支持



關注Java開發寶典

天天學習技術乾貨



點贊是最大的支持 

本文分享自微信公衆號 - 武哥聊編程(eson_15)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索