分布式定时系统 | 青训营笔记

97 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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 编写的开源作业调度框架,其基本体系结构如下图所示:

61.jpeg 它由任务、触发器、调度器组成,可以实现单任务的极致控制,不过其没有负载均衡机制

分布式定时系统

62.jpeg

分布式定时系统主要解决触发、调度、执行三个问题,主要结构包括触发器(解析任务,生成触发事件)、调度器(分配任务,管理任务生命周期)、执行器(执行任务单元,执行任务逻辑)以及控制台(任务管理和干预)组成。下面着重介绍触发器原理。

触发器

对于给定的一系列任务,解析触发规则,在规定的时间点触发任务的调度

触发方案

触发方案包括以下几种:

定时扫描+延时消息

63.jpeg

Scanner定时执行扫描DB,找到到时或者即将到时的任务的任务,放到processor中

时间轮

64.jpeg

如上图所示,使用时间轮存储任务时,每个节点存储同执行时间的任务列表。

65.jpeg

当任务过多导致刻度不够时,可以如上图所示,使用多级时间轮来存储任务,上一级时间轮转过对应刻度后把任务塞到下级的时间轮中

高可用问题

在实际操作中会遇到很多突发问题,比如不同业务间的任务调度互相影响或者机器挂掉。因此,在存储过程中,可以针对不同的业务做出资源隔离,在 运行时,不同业务分开执行,机器部署时,采用多机房集群化部署,通过数据库锁或者分布式锁保证任务只被触发一次。

66.jpeg

上图表示数据库行锁模式,在触发调度之前,更新数据库中jobinstance的状态,成功抢锁才会触发调度。不过会导致多台机器频繁竞争数据库锁,节点越多性能越差。

67.jpeg

上图则表示分布式锁模式,使用的时redis锁,在触发调度前,触发器会尝试抢占分布式锁。该方案性能较高。