传统的任务调度
-
常用的工具
- 简介 优点 缺点 Timer java.utilTimer 工具类 简单易用 单线程来调度,任务间相互挤占影响 Spring-Task spring3.0以后引入的定时任务工具,相当于轻量级的Quartz 用法简单,只需要引入Spring的包 任务执行默认单线程(可配置为多线程)
任务调度为同步模式,上一调度对下一调度 有影响Quartz java事实上的定时任务标准 采用多线程异步调度
满足更多更复杂的调度需要配置使用稍复杂 -
cron表达式
字段 允许值 允许的特殊符号 秒 0~59 整数 , - * / 分 0~59 整数 , - * / 小时 0~23 整数 , - * / 日期 0~31 整数 , - * / ? L W C 月份 0~12 整数 , - * / 星期 1~7 整数 , - * / ? L C # 年(可选,留空) 1970 ~ , - * / *:表示匹配该域的任意值
?: 只能在日期和星期两个域使用,表示匹配域的任意值
-:表示范围
/:表示起始时间开始触发,然后每隔固定时间触发一次
,:表示列出枚举值
L : 表示最后的时间,只能在日期和星期两个域使用
w : 表示有效工作日(周一到周五),只能在日期域中使用
LW : LW两个字符可以连用,表示某个月最后一个工作日,即最后一个星期五
#: 用于确定每个月第几个星期几,只能出现在星期域中。例如 4#2,表示某月的第二个星期三 -
Timer的简单使用
package com.example.taskschedule.timer; import java.util.Timer; import java.util.TimerTask; public class TimerTest { static int i = 1; public static void main(String[] args) { Timer timer = new Timer(); //延迟3秒后,执行一次 timer.schedule(add(), 3000); //延迟1秒后,每隔3秒执行一次 timer.schedule(add(), 1000, 3000); } public static TimerTask add() { return new TimerTask() { @Override public void run() { System.out.println(i++); } }; } }
-
spring-task的简单使用
package com.example.taskschedule.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * 配置Spring-Task的线程池 * 如果不配置线程池Spring-Task的任务调度将默认以单线程的方式运行 * @EnableScheduling 启用Scheduling * */ @Configuration @EnableScheduling public class SpringTaskConfig { @Bean public ThreadPoolTaskScheduler SpringTask() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(10); taskScheduler.setThreadNamePrefix("Spring-Task-Thread"); return taskScheduler; } }
package com.example.taskschedule.springtask; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class SpringTaskTest { //上一个任务结束到下一个任务开始的时间间隔为固定的1秒,任务的执行总是要先等到上一个任务的执行结束 @Scheduled(fixedDelay = 1000) public void add() { System.out.println("当前线程A:" + Thread.currentThread().getId()); } //每间隔1秒钟就会执行任务(如果任务执行的时间超过1秒,则下一个任务在上一个任务结束之后立即执行) @Scheduled(fixedRate = 1000) public void add1() { System.out.println("当前线程B:" + Thread.currentThread().getId()); } //第一次执行的任务将会延迟3秒钟后才会启动 @Scheduled(fixedDelay = 1000, initialDelay = 3000) public void add2() { System.out.println("当前线程C:" + Thread.currentThread().getId()); } //Cron表达式,每个月的15号上午10点15开始执行任务 @Scheduled(cron = "0 15 10 15 * ?") public void add3() { System.out.println("当前线程D:" + Thread.currentThread().getId()); } }
-
Quartz的简单使用
敬请期待
传统定时任务存在的问题
-
业务耦合
如果需要修改定时任务时间,就需要重新部署整个应用将会导致整个应用停止一段时间 -
单点风险
所有调度任务都在单台服务器上执行,当任务执行节点出现问题时,整个定时任务全部终止 -
资源分配不均衡
随着业务越来越多,相应的定时任务也会增多,单台服务器执行任务的压力会越来越大
分布式任务调度
-
常用方案
- 简介 优点 缺点 Elastic - job 当当提供的开源分布式调度工具,封装Quartz,使用Zookeeper 协调任务 通过zookeeper来动态扩容,分片和弹性扩容性能好,业务量大的时候也能非常好的调度 使用和配置复杂,任务控制不灵活 xxl - job "调度中心"基于集群Quartz实现并支持集群部署.
任务分布式执行,任务‘’执行器‘’支持集群部署原理简单、实现简洁、对任务控制更灵活 调度中心通过DB锁保证一致性,执行器的扩展,会增大DB压力 -
Elastic - job的使用
#引入jar <dependency> <groupId>com.github.yinjihuan</groupId> <artifactId>elastic-job-spring-boot-starter</artifactId> <version>1.0.2</version> </dependency> #添加配置mave库地址-(elastic-job-spring-boot-starter 在中央maven库不存在) <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>
# 添加elastic-job的zk注册中心配置 elastic.job.zk.serverLists=127.0.0.1:2181 elastic.job.zk.namespace=enjoy_elastic
#Job类 #name 任务名称,cron 定时规则 #shardingItemParameters 分片参数,shardingTotalCount 分片数 #listener 自定义-ElasticJob作业监听器. 默认 "" (不监听) #jobExceptionHandler 自定义-ElasticJob异常处理器. 默认DefaultJobExceptionHandler #overwrite 是否允许客户端规则参数覆盖注册中心. 默认 false 不允许 @ElasticJobConf(name = "ElasticJob", cron = "0/5 * * * * ?" , shardingItemParameters = "0=beijing,1=shanghai", shardingTotalCount = 2 , listener = "com.example.taskschedule.elasticjob.ElasticJobMsgListener" , jobExceptionHandler = "com.example.taskschedule.elasticjob.ElasticJobExceptionHandler" , overwrite = true) public class ElasticJob implements SimpleJob { @Override public void execute(ShardingContext shardingContext) { System.out.println(Thread.currentThread().getName() + " ,当前分片参数=" + shardingContext.getShardingParameter()); int i = 1 / 0; } }
/** * 自定义 - ElasticJob作业监听器 */ public class ElasticJobMsgListener implements ElasticJobListener { @Override public void beforeJobExecuted(ShardingContexts shardingContexts) { String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); String msg = date + " 【任务开始执行-" + shardingContexts.getJobName() + "】"; System.out.println("before: " + msg); } @Override public void afterJobExecuted(ShardingContexts shardingContexts) { String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); String msg = date + " 【任务执行结束-" + shardingContexts.getJobName() + "】"; System.out.println("after: " + msg); } }
/** * 自定义-ElasticJob异常处理器 */ @Slf4j public class ElasticJobExceptionHandler implements JobExceptionHandler { @Override public void handleException(String jobName, Throwable throwable) { log.error(String.format("Job '%s' exception occur in job processing", jobName), throwable); System.out.println("exception:【" + jobName + "】任务异常。" + throwable.getMessage()); } }
-
xxl - job的使用
敬请期待