任务调度(定时任务)
任务调度
任务调度,简单来说就是定时任务,是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。
Java常见的任务调度方式
基于JDK方式:Timer和ScheduledExecutorService
Time (java.util.Timer) JDK 自带,简单单一,复杂场景不适用。
public class TimeTaskDemo { static long count = 0; public static void main(String[] args) { TimerTask timerTask = new TimerTask() { @Override public void run() { count++; System.out.println("TIME TASK ===>" + count); } }; Timer timer = new Timer(); timer.scheduleAtFixedRate(timerTask, 0, 1000); } }
ScheduledExecutorService
(线程池)JKD 自带,基于线程池实现,可搭配异步(@Asnyc
)。
@RestController public class ScheduledExecutorDemoContoller { //... 省略其他代码 @GetMapping("/testSES") public String ScheduledExecutorService() { ScheduledExecutorService service = Executors.newScheduledThreadPool(10); service.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("ScheduledExecutorService定时任务执行:" + new Date()); } }, 1, 1, TimeUnit.SECONDS); System.out.println("ScheduledExecutorService 定时任务启动:" + new Date()); // 可同时设定多个任务只需再次service.scheduleAtFixedRate(...)...即可 return "ScheduledExecutorService OK"; } }
其他框架
Spring Task
(基于注解@Scheduled
) Spring 提供,支持注解和配置文件形式,支持Cron
表达式,使用简单且功能强大。
@Component @EnableScheduling //开启定时任务 public class ScheduleTaskDemo { @Scheduled(cron = "0/5 * * * * ?") private void configureTasks() { System.out.println("test info ===>" + LocalDateTime.now()); } }
Quartz
功能强大,可实现较复杂调度功能,支持分布式调度,但配置稍复杂。
// Maven依赖 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency>
// 任务调度类,execute 被定时执行某某操作的具体方法 @Component public class TimeJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("quartz job date:" + new Date().getTime()); } }
@RestController public class TestQuatrtzController { // 省略其他代码... @GetMapping("/quartz") public String testQuartz(){ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler scheduler = sf.getScheduler(); JobDetail job = JobBuilder.newJob(TimeJob .class) .withDescription("This is darian job ,welcome you come here ...") .withIdentity("darianJob", "darianGroup") .build(); long time = System.currentTimeMillis() + 3*1000L; //3秒后启动任务 Date startTime = new Date(time); //使用SimpleScheduleBuilder或者CronScheduleBuilder Trigger trigger = TriggerBuilder.newTrigger() .withDescription("") .withIdentity("darianTrigger", "darianTriggerGroup") //.withSchedule(SimpleScheduleBuilder.simpleSchedule()) .startAt(startTime) .withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * * * ? *")) .build(); scheduler.scheduleJob(job, trigger); scheduler.start(); return "OK!"; } }
Corn(Quartz)表达式在线生成:http://cron.qqe2.com/
任务调度的问题
上面我们讲了几种传统给任务调度(定时任务),缺点也很明显,不支持高并发、资源没有最大化利用、没有容错、不方便管理等等。
分布式情况下定时任务的调度会出现那些问题?
最大的问题即:任务调度的幂等性问题,简单来说就是不能被重复执行,所以我们需要一个分布式任务调度平台,可以为我们提供管理、高可用、集群、报警等机制的平台。
分布式任务调度平台
这里介绍的分布式任务调度平台指的是XXL-JOB ,XXL-JOB 是分布式定时任务的解决方案之一。XXL-JOB 功能非常的齐全、上手简单易懂,优点非常多,点击了解 xxl-job
XXL-JOB 采用的概念是:“调度中心” 和 “执行器”。
调度中心:你可以想象成注册中心,所有的任务都需要经过它
执行器:具体的任务执行者
在官网有相应的开发文档,想要使用XXL-JOB ,首先需要前往XXL-JOB GitHub地址:https://github.com/xuxueli/xxl-job 下载相应的压缩包。
快速入门XXL-JOB → xxl-job中文文档
注意: 我在使用XXL-JOB 2.1.2 版本时,环境要求 Mysql 5.7,我使用的是 Mysql 5.6,在执行 XXL-JOB 的 “调度数据库初始化SQL” 时,某张表出错如下:
“ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes”
这个错误,是指超出索引字节的限制,并不是指字段长度限制。在官方文档“Limits on InnoDB Tables”有关于这方面的介绍、描述(详情请见参考Mysql5.6官方文档)。
解决方案:
方案1:首先需要对Mysql做一些调整,然后在执行的SQL添加特定参数。
方案2:升级MySQL至5.7
下面讲的是方案1:
我用的Mysql的自带工具连接操作Msql,你用其他的MySQL可视化工具连接执行也可以。
在执行创建表出错为:“ERROR 1071 : Specified key was too long; max key length is 767 bytes” 的SQL后面添加相应参数,如下图。