作者:竞霄
定时任务作为一种按照约定时间执行预期逻辑的通用模式,在企业级开发中承载着丰富的业务场景,诸如后台定时同步数据生成报表,定时清理磁盘日志文件,定时扫描超时订单进行补偿回调等。程序开发人员在定时任务领域有着诸多框架和方案可供选择,并借此快速实现业务功能实现产品上线。本文将就当前主流定时任务解决方案进行介绍和分析,期望可以在企业技术选型和项目架构重构时作为参考。
Crontab
目标定位
Crontab 作为 Linux 内置的可执行命令,可以实现按照 cron 表达式生成的时间执行指定的系统指令或 shell 脚本。
使用方式
crontab 命令语法:
crontab [-u username] [-l | -e | -r ]
参数:
-u : 只有root用户才能进行这个任务,编辑某个用户的crontab
-e : 编辑 crontab 的工作内容
-l : 查阅 crontab 的工作内容
-r : 移除所有的 crontab 的工作内容
配置文件示例:
* * * * * touch ~/crontab_test
* 3 * * * ~/backup
0 */2 * * * /sbin/service httpd restart
实现原理
crond 守护进程是通过 Linux 启动时的 init 进程启动,由 cornd 每分钟会检查/etc/crontab 配置文件中是否有需要执行的任务,并通过 /var/log/cron 文件输出定时任务的执行情况。用户可以使用 Crontab 命令管理/etc/crontab 配置文件。
方案分析
借助 Crontab 用户可以十分便利的快速实现简易的定时任务功能,但存在以下痛点:
- 定时任务与指定 linux 机器绑定,当机器扩容或者更换时需要重新配置 contab,同时存在单点故障风险
- 随着定时任务规模增多,无统一视角对其进行任务进度的追踪和管控,难以维护
- 功能过于简单,没有超时,重试,阻塞等任务高级特性
- 可观测能力差,问题排查定位困难
- 任务常驻,当无任务执行时造成不必要的资源成本浪费
Spring Task
目标定位
Spring 框架提供了开箱即用的定时调度功能,用户可以通过 xml 或者@Scheduled 注解的方式标识指定方法执行的周期。Spring Task 支持多种任务执行模式,包括带时区配置的 corn,固定延迟,固定速率等。
使用方式
代码实例如下:
@EnableScheduling
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@Component
public class MyTask {
@Scheduled(cron = "0 0 1 * * *")
public void test() {
System.out.println("test");
}
}
实现原理
Spring Task 的原理是在初始化 bean 时借助 ScheduledAnnotationBeanPostProcessor 拦截@Scheduled 注解所标识的方法,并根据每个方法及其注解配置构建相应的 Task 实例注册到 ScheduledTaskRegistrar 中,并在单例 bean 初始化完成后通过 afterSingletonsInstantiate