Spring生态研习【一】:定时任务Spring-task

本系列具体研究一下spring生态中的重要或者常用的功能套件,今天从定时任务开始,主要是spring-task。至于quartz,下次找个时间再总结。

我的验证环境,是SpringCloud体系下,基于SpringBoot进行的。Spring-boot的版本:1.5.4.release. JDK:1.8, 其他不多说。主要是基于注解的模式实现验证,基于spring-boot吗,就用他的约定大于配置以及注解配置。

今天重点介绍一下Spring task的三种典型的应用模式。实验项目,基于IDEA进行,创建一个Spring-Task的父项目(project),然后分别创建相应的三种应用模式的子项目(Module),在父项目中进行基础的pom.xml的配置。具体的创建过程,不是这里的重点,不做介绍。下面直接给出父项目的pom.xml内容:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.</modelVersion> <groupId>com.roomdis</groupId>
<artifactId>springtask</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>annotation-task</module>
<module>change-scheduler</module>
<module>dynamic-task</module>
</modules> <name>SpringTask</name>
<description>Spring Cloud project</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5..RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

1. 静态定时

这种模式下,有三种典型的应用,即spring task的@Schedule注解的三个配置类型。cron,fixRate,fixDelay

1.1 pom.xml内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springtask</artifactId>
<groupId>com.roomdis</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.</modelVersion>
<artifactId>annotation-task</artifactId>
<packaging>jar</packaging> <name>annotation-task</name>
<description>Spring Cloud project</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-core</artifactId>
</dependency>
</dependencies>
</project>

基于Spring-cloud进行定时任务研究,最重要的核心依赖就是spring-cloud-task-core.

1.2 spring工程代码

package com.roomdis.springtask.annotationtask;

import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import java.text.SimpleDateFormat;
import java.util.Date; /**
* Created by chengsh05 on 2018-07-06.
*
* 重点研究Scheduled的几个注解参数的用法:
*
* 可以看出该注解有三个方法或者叫参数,分别表示的意思是:
*
* cron:指定cron表达式
*
* fixedDelay:官方文档解释:An interval-based trigger where the interval is measured from the completion time of the previous task. The time unit value is measured in milliseconds.
* 即表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。
*
* fixedRate:官方文档解释:An interval-based trigger where the interval is measured from the start time of the previous task. The time unit value is measured in milliseconds.
* 即从上一个任务开始到下一个任务开始的间隔,单位是毫秒。
*
*/
@Component
public class SchTask { private Logger logger = Logger.getLogger(SchTask.class); @Scheduled(cron = "3/5 * * * * *")
public void taskCron(){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
simpleDateFormat.applyPattern("yyyy-MM-dd HH:mm:ss");
String sdf = simpleDateFormat.format(new Date());
logger.info("cron: " + sdf);
} @Scheduled(fixedRate = )
public void taskFixedRate(){
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
simpleDateFormat.applyPattern("yyyy-MM-dd HH:mm:ss");
String sdf = simpleDateFormat.format(new Date());
logger.info("fixedRate: " + sdf);
} @Scheduled(fixedDelay = )
public void taskFixedDelay(){
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
simpleDateFormat.applyPattern("yyyy-MM-dd HH:mm:ss");
String sdf = simpleDateFormat.format(new Date());
logger.info("fixedDelay: " + sdf);
} }

然后,再写一个Springboot的启动程序:

package com.roomdis.springtask;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling; /**
* Created by chengsh05 on 2018-07-06.
*/
@SpringBootApplication
@EnableScheduling
public class AnnotationTaskApplication { public static void main(String []args) {
SpringApplication.run(AnnotationTaskApplication.class, args);
}
}

这个程序,启动后,定时任务即开始运行,所以定义成静态定时任务。且定时任务计划,即cron表达的信息固定不可变。

1.3 运行结果

注意,这里的定时任务SchTask里面,有3个不同的定时任务,我在测试中,分别将三个任务独立启动运行,得到了相应的输出日志,供分析和验证。从输出的日志,可以辅助理解cron的定时规则,即cron的正则表达的书写以及含义,后面将会附上介绍。独立运行的日志如下:

cron:

 .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.4.RELEASE) -- ::28.089 INFO --- [ main] c.r.s.AnnotationTaskApplication : Starting AnnotationTaskApplication on -- with PID (D:\Knowledge\SOURCE\spring-task\annotation-task\target\classes started by chengsh05 in D:\Knowledge\SOURCE\spring-task)
-- ::28.091 INFO --- [ main] c.r.s.AnnotationTaskApplication : No active profile set, falling back to default profiles: default
-- ::28.147 INFO --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2698dc7: startup date [Fri Jul :: CST ]; root of context hierarchy
-- ::28.974 INFO --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
-- ::28.980 INFO --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
-- ::28.989 INFO --- [ main] c.r.s.AnnotationTaskApplication : Started AnnotationTaskApplication in 1.2 seconds (JVM running for 1.545)
2018-07-06 19:25:32.997 INFO 8920 --- [pool-1-thread-1] c.r.springtask.annotationtask.SchTask : cron: 2018-07-06 19:25:32
2018-07-06 19:25:37.996 INFO 8920 --- [pool-1-thread-1] c.r.springtask.annotationtask.SchTask : cron: 2018-07-06 19:25:37
2018-07-06 19:25:42.996 INFO 8920 --- [pool-1-thread-1] c.r.springtask.annotationtask.SchTask : cron: 2018-07-06 19:25:42
2018-07-06 19:25:47.996 INFO 8920 --- [pool-1-thread-1] c.r.springtask.annotationtask.SchTask : cron: 2018-07-06 19:25:47

fixRate:

-- ::21.575  INFO  --- [pool--thread-] c.r.springtask.annotationtask.SchTask    : fixedRate: -- ::
-- ::24.572 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedRate: -- ::
-- ::27.569 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedRate: -- ::
-- ::30.566 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedRate: -- ::
-- ::33.563 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedRate: -- ::
-- ::36.560 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedRate: -- ::

fixDelay:

-- ::37.492  INFO  --- [pool--thread-] c.r.springtask.annotationtask.SchTask    : fixedDelay: -- ::
-- ::42.488 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedDelay: -- ::
-- ::47.484 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedDelay: -- ::
-- ::52.480 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedDelay: -- ::
-- ::57.476 INFO --- [pool--thread-] c.r.springtask.annotationtask.SchTask : fixedDelay: -- ::

其中重点,对比fixRate和fixDelay,注意区分其含义,fixRate是以固定频率启动项目,即周期的计算是基于任务启动的时间点,而fixDelay是基于任务执行结束的时间点进行周期。从输出日志清晰的得到验证。

1.4 cron模式正则信息说明

Cron的正则表达,可以有下面的两种模式,但是往往用到的第2种更多点。

1、Seconds Minutes Hours DayofMonth Month DayofWeek Year

2、Seconds Minutes Hours DayofMonth Month DayofWeek

例如上面,我们的例子中,3/5 * * * * *

从左到右,每一个字段都有一套可以指定有效值,如

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”表示

●星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;

●问号(?):该字符只在日期和星期字段中使用,虽然我现在不知道它的值是多少,但是它的值是唯一的,通过日期可以推出星期,通过本周是周几也可以推出日期。

●减号(-):表达一个范围,如在小时字段中使用“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在星期字段中相当于星期日后的第一天。

2. 修改定时器(工程启动后定时任务即运行,后续可以基于需要用REST API对定时规则进行修改)

2.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springtask</artifactId>
<groupId>com.roomdis</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.</modelVersion>
<artifactId>changeshecduler</artifactId>
<packaging>jar</packaging> <name>change-scheduler</name>
<description>Spring Cloud project</description> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-core</artifactId>
</dependency>
</dependencies> </project>

2.2 定时任务代码

package com.roomdis.springtask.changescheduler.controller;

import org.apache.log4j.Logger;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import java.util.Date; /**
* Created by chengsh05 on 2018-07-06.
*/
@RestController
@EnableScheduling
public class SchController implements SchedulingConfigurer { private Logger logger = Logger.getLogger(SchController.class); /**
* 定时任务的定时器表达式: 秒 分 时 日期 月 星期
* 注意:有的地方说定时正则表达式可以有year,即7个元素,但是,在spring-boot里面,只能是6个元素,没有年。
*/
private String cronExpression = "1/5 * * * * *"; /**
* 通过REST API请求对参数进行修改,定时规则进行调整
*
* @param exp
* @return
*/
@RequestMapping("change")
public String change(@RequestParam("exp") String exp) {
cronExpression = exp;
logger.info("new cron expression: " + exp);
return cronExpression;
} /**
* 定时任务要执行的方法
*
* @return
*/
private Runnable getTask() {
Runnable task = new Runnable() {
@Override
public void run() {
logger.info("Worker tell you the time: " + new Date());
}
};
return task;
} /**
* 调度实现的时间控制
*
* @param scheduledTaskRegistrar
*/
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
Trigger trigger=new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
CronTrigger cronTrigger=new CronTrigger(cronExpression);
return cronTrigger.nextExecutionTime(triggerContext);
}
};
scheduledTaskRegistrar.addTriggerTask(getTask(), trigger);
}
}

基于springboot的主程序,在这里就不多写了。注意,本工程中,cron的表达式,尝试用7个字段的书写方式,结果不行,即不能用有year的信息。错误如下:

java.lang.IllegalArgumentException: Cron expression must consist of 6 fields (found 7 in "1/5 * * * * * *")
at org.springframework.scheduling.support.CronSequenceGenerator.parse(CronSequenceGenerator.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.support.CronSequenceGenerator.<init>(CronSequenceGenerator.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.support.CronSequenceGenerator.<init>(CronSequenceGenerator.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at com.roomdis.springtask.changescheduler.controller.SchController$.nextExecutionTime(SchController.java:) ~[classes/:na]
at org.springframework.scheduling.concurrent.ReschedulingRunnable.schedule(ReschedulingRunnable.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.concurrent.ConcurrentTaskScheduler.schedule(ConcurrentTaskScheduler.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleTriggerTask(ScheduledTaskRegistrar.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleTasks(ScheduledTaskRegistrar.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:) ~[spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:) ~[spring-context-4.3..RELEASE.jar:4.3..RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:) ~[spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:) [spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:) [spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:) [spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:) [spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:) [spring-boot-1.5..RELEASE.jar:1.5..RELEASE]
at com.roomdis.springtask.changescheduler.ChangeSchApplication.main(ChangeSchApplication.java:) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_77]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:) ~[na:1.8.0_77]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:) ~[na:1.8.0_77]
at java.lang.reflect.Method.invoke(Method.java:) ~[na:1.8.0_77]
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:) [idea_rt.jar:na] -- ::02.244 INFO --- [ main] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2bbaf4f0: startup date [Fri Jul :: CST ]; root of context hierarchy
-- ::02.247 INFO --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown

2.3 输出验证

启动主程序后,会看到前面的默认运行规则,即基于1/5 * * * * *得到的黑色加粗区域,然后,在浏览器的地址栏执行http://localhost:8080/change?exp=1/3 * * * * *,然后回车,即得到红色区域的执行输出:

-- ::59.173  INFO  --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
-- ::59.183 INFO --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
-- ::59.230 INFO --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): (http)
-- ::59.233 INFO --- [ main] c.r.s.c.ChangeSchApplication : Started ChangeSchApplication in 2.47 seconds (JVM running for 2.825)
-- ::01.001 INFO --- [pool--thread-] c.r.s.c.controller.SchController : Worker tell you the time: Fri Jul :: CST
-- ::06.000 INFO --- [pool--thread-] c.r.s.c.controller.SchController : Worker tell you the time: Fri Jul :: CST

-- ::06.352 INFO --- [nio--exec-] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
-- ::06.352 INFO --- [nio--exec-] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
-- ::06.366 INFO --- [nio--exec-] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in ms
2018-07-06 19:39:06.389 INFO 12704 --- [nio-8080-exec-1] c.r.s.c.controller.SchController : new cron expression: 1/3 * * * * *
2018-07-06 19:39:11.001 INFO 12704 --- [pool-1-thread-1] c.r.s.c.controller.SchController : Worker tell you the time: Fri Jul 06 19:39:11 CST 2018
2018-07-06 19:39:13.000 INFO 12704 --- [pool-1-thread-1] c.r.s.c.controller.SchController : Worker tell you the time: Fri Jul 06 19:39:13 CST 2018
2018-07-06 19:39:16.001 INFO 12704 --- [pool-1-thread-1] c.r.s.c.controller.SchController : Worker tell you the time: Fri Jul 06 19:39:16 CST 2018

3. 动态启动和停止以及修改定时规则

3.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springtask</artifactId>
<groupId>com.roomdis</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.</modelVersion>
<packaging>jar</packaging>
<artifactId>dynamic-task</artifactId>
<description>Spring Cloud project</description> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-core</artifactId>
</dependency>
</dependencies> </project>

3.2 主体工程代码

package com.roomdis.springtask.dynamictask.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import java.util.Date;
import java.util.concurrent.ScheduledFuture; /**
* Created by chengsh05 on 2018-07-06.
*/
@RestController
@EnableScheduling
public class DynamicController { private Logger logger = Logger.getLogger(DynamicController.class); @Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler; private ScheduledFuture<?> scheduledFuture; @Bean
public ThreadPoolTaskScheduler getThreadPoolTaskScheduler(){
return new ThreadPoolTaskScheduler();
} private Runnable getTask() {
Runnable task = new Runnable() {
@Override
public void run() {
logger.info("Worker tell you the time: " + new Date());
}
};
return task;
} /**
* 核心是利用ThreadPoolTaskScheduler的schedule()函数启动,返回一个ScheduledFeature。
*
* @return
*/
@RequestMapping("/start")
public String startTask(){
/**
* task:定时任务要执行的方法
* trigger:定时任务执行的时间
*/
scheduledFuture = threadPoolTaskScheduler.schedule(getTask(), new CronTrigger("0/5 * * * * *") );
logger.info("start task done");
return "start task done";
} /**
* 核心是利用ScheduledFeature的cancel()函数。
*
* @return
*/
@RequestMapping("stop")
public String stopTask(){
if(scheduledFuture != null){
/**
* ScheduledFeature继承了jdk的接口Future, cancel用到参数true表示强制关闭任务。
* cancel的参数false,表示允许任务执行完毕。
* 因为这里是周期任务,没有执行完毕的时候,所以用的是强制关闭任务。
*/
scheduledFuture.cancel(true);
}
logger.info("stop task done");
return "stop task done";
} @RequestMapping("/change")
public String changeTask(@RequestParam("exp") String exp){
//1. 停止定时器
stopTask();
//2. 修改任务执行计划
scheduledFuture=threadPoolTaskScheduler.schedule(getTask(), new CronTrigger(exp) );
//3. 启动定时器
startTask();
logger.info("change task done");
return "change task done";
}
}

代码已经很清晰的表达了处理逻辑:

  • 其实,这个地方,主要是利用ThreadPoolTaskScheduler的功能,它可以schedule任务,参数是Runnable的task以及CronTrigger的定时触发器最后利用schedule函数的返回值ScheduleFeature的cancel函数实现定时任务的停止定时任务的启动,其实就是schedule()函数执行,就启动了定时计划。通过外部REST API控制定时任务的创建和启动,同样也就可以实现通过REST API实现定时任务的停止。
  • 最重要的一个点是修改定时任务计划,这里,主要是先停止当前正在运行的任务,然后修改调度任务,最后,再启动任务
  • 主程序启动后,定时任务默认是不运行的,只有通过外部的控制,这里是REST API实现的定时任务的控制,这个特性非常重要,且有价值。

总结:

1. spring task应用非常有价值,且使用很简单,只是要注意cron正则的书写,且一定要注意使用技巧,上面有对cron的规则简介。

2. spring task的定时任务,只能用在单机,确切的说是单应用的系统,在分布式系统里面,可以采用中心节点,对spring task的应用进行全局控制,当然,这个只是一种思路。

上一篇:第1天:CSS基本样式


下一篇:HDU 5726 GCD (2016多校、二分、ST表处理区间GCD、数学)