本系列具体研究一下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的应用进行全局控制,当然,这个只是一种思路。

Spring生态研习【一】:定时任务Spring-task的更多相关文章

  1. Spring生态研习【二】:SpEL(Spring Expression Language)

    1. SpEL功能简介 它是spring生态里面的一个功能强大的描述语言,支在在运行期间对象图里面的数据查询和数据操作.语法和标准的EL一样,但是支持一些额外的功能特性,最显著的就是方法调用以及基本字 ...

  2. Spring生态研习【四】:Springboot+mybatis(探坑记)

    这里主要是介绍在springboot里面通过xml的方式进行配置,因为xml的配置相对后台复杂的系统来说,能够使得系统的配置和逻辑实现分离,避免配置和代码逻辑过度耦合,xml的配置模式能够最大限度的实 ...

  3. Spring生态研习【三】:Spring-kafka

    1. 基本信息介绍 基于spring的kafka应用,非常简单即可搭建起来,前提是要有一个kafka的broker集群.我在之前的博文里面已经介绍并搭建了一套broker环境,参考Kafka研究[一] ...

  4. Spring生态研习【五】:Springboot中bean的条件注入

    在springboot中,开发的确变的简单了很多,但是,开发者现在希望开发傻瓜式的方便搞定项目中的各种奇怪的需求最好了,不用烧脑,本来程序猿的生活就是枯燥的,不要再给自己添加更多的烦恼. 今天,就为了 ...

  5. 定时任务spring task

    1)spring boot 几种定时任务的实现方式:https://www.jianshu.com/p/b6809b5a0c26 2)spring-boot 定时任务之Scheduled Task:h ...

  6. 基于spring+quartz的分布式定时任务框架

    问题背景 我公司是一个快速发展的创业公司,目前有200人,主要业务是旅游和酒店相关的,应用迭代更新周期比较快,因此,开发人员花费了更多的时间去更=跟上迭代的步伐,而缺乏了对整个系统的把控 没有集群之前 ...

  7. spring注解scheduled实现定时任务

    只想说,spring注解scheduled实现定时任务使用真的非常简单. 一.配置spring.xml文件 1.在beans加入xmlns:task="http://www.springfr ...

  8. spring项目中的定时任务实现和问题解决

    之前我用JAVA中的Timer类实现了服务器的定时任务,具体详见之前的博文. 后来发现了一个更简单的实现方式,利用spring中的@Scheduled注解实现觉得简单的很多. 确实spring封装的特 ...

  9. Spring Boot(九):定时任务

    Spring Boot(九):定时任务 一.pom包配置 pom包里面只需要引入springboot starter包即可 <dependencies> <dependency> ...

随机推荐

  1. FileProvider 添加二级目录

    我们在做Android N升级适配的时候 传统的Intent调用文件的方式会被认为不安全的 然后系统需要让我们使用更加安全的FileProvider的方法去构建intent请求 如 拍照,安装新的ap ...

  2. 组件、框架、Packagist、Composer

    组件是一组打包的代码,是一系列相关的类.接口和Trait,用于帮助我们解决PHP应用中某个具体问题. 优秀的PHP组件具备以下特性: 作用单一:专注于解决一个问题,而且使用简单的接口封装功能 小型:小 ...

  3. 『计算机视觉』mini深度学习框架实现

    一.项目简介 手动实现mini深度学习框架,主要精力不放在运算优化上,仅体会原理. 地址见:miniDeepFrame 相关博客 『TensorFlow』卷积层.池化层详解 『科学计算』全连接层.均方 ...

  4. spring基础知识,未完待续

    https://blog.csdn.net/slow_wakler/article/details/54895508   http://www.runoob.com/design-pattern/ch ...

  5. Redis缓存设计及常见问题

    Redis缓存设计及常见问题 缓存能够有效地加速应用的读写速度,同时也可以降低后端负载,对日常应用的开发至关重要.下面会介绍缓存使 用技巧和设计方案,包含如下内容:缓存的收益和成本分析.缓存更新策略的 ...

  6. 【转】阿里云免费SSL证书申请与安装使用(IIS7)

    阅读目录 准备: 第一步:免费SSL证书申请 第二步:证书导入 第三步:分配服务器证书 最后一步:访问测试 回到顶部 准备: 阿里云已完成备案的域名一个 回到顶部 第一步:免费SSL证书申请 登陆阿里 ...

  7. 由通过seeion识别保存在cookie中的seeionID引发的CSRF问题

    上图是一个完整的CSRF攻击过程解释图 重点是第三句话 用户在没有登出的情况下,被攻击者获得了SESSIONID信息,伪造真用户登录 二.CSRF防御 通过 referer.token 或者 验证码 ...

  8. SQL DELETE 语句详解

    SQL DELETE 语句详解   DELETE 语句 DELETE 语句用于删除表中的行. 语法 DELETE FROM 表名称 WHERE 列名称 = 值 Person: LastName Fir ...

  9. Linux下如何查看tomcat是否安装、启动、文件路径、进程ID

    Linux下如何查看tomcat是否安装.启动.文件路径.进程ID 在Linux系统下,Tomcat使用命令的操作! 检测是否有安装了Tomcat: rpm -qa|grep tomcat 查看Tom ...

  10. 使用json改写网站

    效果图 json文件 https://raw.githubusercontent.com/sherryloslrs/timetable/master/timetable.json { "Ti ...