原文連接: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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。