这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
定时系统历史
Linux命令-CronJob
Cron 是UNIX, SOLARIS,LINUX下的一个十分有用的工具。通过Cron脚本能使计划任务定期地在系统后台自动运行。这种计划任务在UNIX, SOLARIS, LINUX下术语为cron jobs。
语法为:
* * * * * command to be executed
- - - - -
| | | | |
| | | | +----- day of week (0 - 6) (Sunday=0) //星期
| | | +------- month (1 - 12) //月
| | +--------- day of month (1 - 31) //日
| +----------- hour (0 - 23) //时
+------------- min (0 - 59) //分
比如:
30 18 * * * rm /home/someuser/tmp/*
表示每天18:30删除存储在/home/someuser/tmp/下的临时文件
Timer和Ticker
Timer是一个定时器,代表未来的一个单一事件,通过Timer本身提供的管道将事件传递出去,言外之意是只执行一次。
其数据结构为:
type Timer struct {
C **<-chan Time //<-chan Time表示时间类型的信道**
r runtimeTimer
}
Ticker是周期性定时器,即周期性的触发一个事件,通过Ticker本身提供的管道将事件传递出去,言外之意是可以重复执行。
type Ticker struct {
C <-chan Time
r runtimeTimer
}
单机定时任务-ScheduledExecutorService
ScheduledExecutorService的主要作用就是可以将定时任务与线程池功能结合使用。而Timer的内部只有一个线程,如果有多个任务的话就会顺序执行,这样我们的延迟时间和循环时间就会出现问题。因此,在对延迟任务和循环任务要求严格的时候,就需要考虑使用ScheduledExecutorService了。
任务调度- Quartz
Quartz 是一个完全由 Java 编写的开源作业调度框架,其基本体系结构如下图所示:
它由任务、触发器、调度器组成,可以实现单任务的极致控制,不过其没有负载均衡机制
分布式定时系统
分布式定时系统主要解决触发、调度、执行三个问题,主要结构包括触发器(解析任务,生成触发事件)、调度器(分配任务,管理任务生命周期)、执行器(执行任务单元,执行任务逻辑)以及控制台(任务管理和干预)组成。下面着重介绍触发器原理。
触发器
对于给定的一系列任务,解析触发规则,在规定的时间点触发任务的调度
触发方案
触发方案包括以下几种:
定时扫描+延时消息
Scanner定时执行扫描DB,找到到时或者即将到时的任务的任务,放到processor中
时间轮
如上图所示,使用时间轮存储任务时,每个节点存储同执行时间的任务列表。
当任务过多导致刻度不够时,可以如上图所示,使用多级时间轮来存储任务,上一级时间轮转过对应刻度后把任务塞到下级的时间轮中
高可用问题
在实际操作中会遇到很多突发问题,比如不同业务间的任务调度互相影响或者机器挂掉。因此,在存储过程中,可以针对不同的业务做出资源隔离,在 运行时,不同业务分开执行,机器部署时,采用多机房集群化部署,通过数据库锁或者分布式锁保证任务只被触发一次。
上图表示数据库行锁模式,在触发调度之前,更新数据库中jobinstance的状态,成功抢锁才会触发调度。不过会导致多台机器频繁竞争数据库锁,节点越多性能越差。
上图则表示分布式锁模式,使用的时redis锁,在触发调度前,触发器会尝试抢占分布式锁。该方案性能较高。