(題號丟失)校門外的樹
阿新 • • 發佈:2020-11-22
這篇文章主要給大家介紹了關於Java學習教程之定時任務全家桶的相關資料,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧!
定時任務應用非常廣泛,Java提供的現有解決方案有很多。
本次主要講schedule、quartz、xxl-job、shedlock等相關的程式碼實踐。
一、SpringBoot使用Schedule
核心程式碼:
1 @Component 2 public class ScheduleTask { 3 4 private Logger logger = LoggerFactory.getLogger(ScheduleTask.class); 5 6 @Scheduled(cron = "0/1 * * * * ? ") 7 public void one() { 8 9 logger.info("one:" + new Date()); 10 } 11 12 @Scheduled(cron = "0/1 * * * * ? ") 13 public void two() { 14 15 logger.info("two:" + new Date()); 16 } 17 18 19 @Scheduled(cron = "0/1 * * * * ? ") 20 public void three() {21 22 logger.info("three:" + new Date()); 23 } 24 }
執行效果如下:
除此之外還可以這樣實現,核心程式碼:
1 @PropertySource(value = { 2 "classpath:task.properties", 3 }, encoding = "utf-8") 4 @Component("scheduleTask") 5 public class ScheduleTask implements SchedulingConfigurer { 6 7 8 @Value("${TEST_JOB_TASK_CRON}")9 private String cron; 10 11 @Override 12 public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { 13 14 scheduledTaskRegistrar.addTriggerTask(new Runnable() { 15 16 @Override 17 public void run() { 18 System.out.println("執行任務:" + DateUtil.date()); 19 20 21 } 22 23 }, new Trigger() { 24 @Override 25 public Date nextExecutionTime(TriggerContext triggerContext) { 26 return new CronTrigger(cron).nextExecutionTime(triggerContext); 27 } 28 }); 29 } 30 31 public void setCron(String cron) { 32 this.cron = cron; 33 } 34 }
有朋友或許很疑惑,為什麼要寫這麼一大堆,這個與前面的程式碼又有何區別呢?
區別是多執行緒並行。其實多執行緒並行也可以不用這麼寫,只需寫一段核心配置類程式碼即可。
定時任務多執行緒配置類:
1 @Configuration 2 public class ScheduleConfig implements SchedulingConfigurer { 3 4 public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { 5 scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5)); 6 } 7 }
再次啟動,檢視效果,如下:
由此看出走不同的執行緒執行,不同的執行緒執行的好處是,如果某一個執行緒掛掉後,並不會阻塞導致其它定時任務無法執行。
另外如果要想併發執行,前面的配置可以不要,直接用SpringBoot提供的現成註解即可,核心程式碼如下:
1 @Component 2 @EnableAsync 3 public class ScheduleAsyncTask { 4 5 private Logger logger = LoggerFactory.getLogger(ScheduleAsyncTask.class); 6 7 @Scheduled(cron = "0/1 * * * * ? ") 8 @Async 9 public void one() { 10 11 logger.info("one Async:" + new Date()); 12 } 13 14 @Scheduled(cron = "0/1 * * * * ? ") 15 @Async 16 public void two() { 17 18 logger.info("two Async:" + new Date()); 19 } 20 21 22 @Scheduled(cron = "0/1 * * * * ? ") 23 @Async 24 public void three() { 25 26 logger.info("three Async:" + new Date()); 27 } 28 }
除此外,還有基於schedule動態定時任務(所謂動態只不過是指cron表示式放在對應的資料表裡),簡單示例程式碼:
1 @Configuration 2 public class DynamicScheduleTask implements SchedulingConfigurer { 3 4 @Autowired 5 @SuppressWarnings("all") 6 CronMapper cronMapper; 7 8 @Mapper 9 public interface CronMapper { 10 @Select("select cron from cron limit 1") 11 public String getCron(); 12 } 13 14 /** 15 * 執行定時任務. 16 */ 17 public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 18 19 taskRegistrar.addTriggerTask( 20 //1.新增任務內容(Runnable) 21 () -> System.out.println("執行動態定時任務: " + LocalDateTime.now().toLocalTime()), 22 //2.設定執行週期(Trigger) 23 triggerContext -> { 24 //2.1 從資料庫獲取執行週期 25 String cron = cronMapper.getCron(); 26 //2.2 合法性校驗. 27 if (StringUtils.isEmpty(cron)) { 28 // Omitted Code .. 29 } 30 //2.3 返回執行週期(Date) 31 return new CronTrigger(cron).nextExecutionTime(triggerContext); 32 } 33 ); 34 } 35 36 37 38 }
核心配置檔案(application.yml):
1 spring: 2 datasource: 3 url: jdbc:mysql://127.0.0.1:3306/test 4 username: root 5 password: 1234
SQL指令碼:
1 DROP DATABASE IF EXISTS `test`; 2 CREATE DATABASE `test`; 3 USE `test`; 4 DROP TABLE IF EXISTS `cron`; 5 CREATE TABLE `cron` ( 6 `cron_id` varchar(30) NOT NULL PRIMARY KEY, 7 `cron` varchar(30) NOT NULL 8 ); 9 INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');
執行效果如下:
二、SpringBoot使用Quartz
1.Maven依賴
1 2 3 4 5 |
<!--引入quartz定時框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
|
2.配置檔案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
spring:
quartz:
#相關屬性配置
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class : org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
clusterCheckinInterval: 10000
useProperties: false
threadPool:
class : org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#資料庫方式
job-store-type: jdbc
#初始化表結構
jdbc:
initialize-schema: always
datasource:
url: jdbc:mysql: //127.0.0.1:3306/test
username: root
password: 1234
|
3.啟動類
1 2 3 4 5 6 7 8 9 10 |
@SpringBootApplication
@EnableScheduling
public class BlogQuartzApplication {
public static void main(String[] args) {
SpringApplication.run(BlogQuartzApplication. class , args);
}
}
|
4.配置類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Configuration
public class QuartzConfiguration {
// 使用jobDetail包裝job
@Bean
public JobDetail myCronJobDetail() {
return JobBuilder.newJob(CouponTimeOutJob. class ).withIdentity( "couponTimeOutJob" ).storeDurably().build();
}
// 把jobDetail註冊到Cron表示式的trigger上去
@Bean
public Trigger CronJobTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule( "0/1 * * * * ?" );
return TriggerBuilder.newTrigger()
.forJob(myCronJobDetail())
.withIdentity( "CouponTimeOutJobTrigger" )
.withSchedule(cronScheduleBuilder)
.build();
}
}
|
5.定時任務類
1 2 3 4 5 6 |
public class CouponTimeOutJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println( "定時任務執行" );
}
}
|
6.啟動成功不報錯
(1)對應的資料庫會生成定時任務相關的資料表
(2)控制檯不斷輸出定時任務執行日誌
三、SpringBoot使用xxl-job
之前寫過一樣的例子,如今簡化了下。
關於xxl-job使用詳情,可以參考我的這篇文章:
1.Maven依賴
1 2 3 4 5 |
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version> 2.2 . 0 </version>
</dependency>
|
2.配置類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig. class );
@Value ( "${xxl.job.admin.addresses}" )
private String adminAddresses;
@Value ( "${xxl.job.executor.appname}" )
private String appName;
@Value ( "${xxl.job.executor.ip}" )
private String ip;
@Value ( "${xxl.job.executor.port}" )
private int port;
@Value ( "${xxl.job.accessToken}" )
private String accessToken;
@Value ( "${xxl.job.executor.logpath}" )
private String logPath;
@Value ( "${xxl.job.executor.logretentiondays}" )
private int logRetentionDays;
@Bean (initMethod = "start" , destroyMethod = "destroy" )
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info( ">>>>>>>>>>> xxl-job config init." );
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
|
3.配置檔案內容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# web port
server.port= 8081
# no web
#spring.main.web-environment= false
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http: //127.0.0.1:8080/xxl-job-admin
### xxl-job, access token
xxl.job.accessToken=
### xxl-job executor appname
xxl.job.executor.appname=blog-job-xxl-job
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
xxl.job.executor.port= 8888
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays= 30
|
4.定時任務類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Component
public class XxlJobTaskExample {
@XxlJob("blogJobHandler")
public ReturnT< String > blogJobHandler(String param) throws Exception {
System.out.println("執行");
XxlJobLogger.log("XXL-JOB, Hello World.");
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
}
}
|
5.執行效果
分別如下所示:
四、SpringBoot使用ShedLock
1.匯入Maven依賴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!-- 分散式定時任務鎖 -->
<!-- https: //mvnrepository.com/artifact/net.javacrumbs.shedlock/shedlock-spring -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version> 4.0 . 4 </version>
</dependency>
<!-- 使用redis做分散式任務 -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-redis-spring</artifactId>
<version> 2.5 . 0 </version>
</dependency>
<!-- redis元件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
|
2.編寫配置類
1 2 3 4 5 6 7 8 9 10 11 |
@Configuration
@EnableSchedulerLock (defaultLockAtMostFor = "PT30M" )
public class ShedLockConfig {
@Bean
public LockProvider lockProvider(RedisTemplate redisTemplate) {
return new RedisLockProvider(redisTemplate.getConnectionFactory());
}
}
|
3.編寫具體的定時任務
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Component
public class TaskSchedule {
/**
* 每分鐘執行一次
* [秒] [分] [小時] [日] [月] [周] [年]
*/
@Scheduled (cron = "1 * * * * ?" )
@SchedulerLock (name = "synchronousSchedule" )
public void SynchronousSchedule() {
System.out.println( "Start run schedule to synchronous data:" + new Date());
}
}
|
4.編寫啟動類
1 2 3 4 5 6 7 |
@SpringBootApplication
@EnableScheduling
public class ShedLockRedisApplication {
public static void main(String[] args) {
SpringApplication.run(ShedLockRedisApplication. class );
}
}
|
5.配置檔案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server:
tomcat:
uri-encoding: UTF- 8
max-threads: 1000
min-spare-threads: 30
port: 8083
spring:
redis:
database: 0
host: localhost
port: 6379
password: # 密碼(預設為空)
timeout: 6000ms # 連線超時時長(毫秒)
jedis:
pool:
max-active: 1000 # 連線池最大連線數(使用負值表示沒有限制)
max-wait: -1ms # 連線池最大阻塞等待時間(使用負值表示沒有限制)
max-idle: 10 # 連線池中的最大空閒連線
min-idle: 5 # 連線池中的最小空閒連線
|
6.測試
我之所以用shedlock是因為確保在叢集環境下各微服務的定時任務只執行一個,而不是全部都執行相同的定時任務。
本次測試效果如下: