Cron 模块构思
需求点
- 单个时间点的定时任务: 每周的某几天的某个时刻执行某个任务
- 多个时间点的定时任务:每周的某几天的某几个时刻执行某个任务
- 单个时间段的频率任务:每周的某几天的某个时间段,以固定的频率重复执行某个任务
- 多个时间段的频率任务:每周的某几天的某几个时间段,以固定的频率重复执行某个任务
选型
- 依赖于Cron模块,如本机cron功能,或者框架的cron功能
- 优点: 使用较为简单
- 缺点:需要解决集群情况下,多服务器重复调用执行的问题。如果是系统的crontab功能,很难针对某一条cronjob做处理,如果是框架的cron则无此问题。
- 依赖于cloudwatch的schedule功能
- 优点:不用担心集群重复调用问题。
- 缺点:代码量增多、额外的云端运维成本、需要额外的lambda函数支持、如果用户回调不是纯函数,会丢失对应上下文环境
模块定义
总览
- 模块入口
interface CronModule {
create(options:CreateOptionsType):Promise<Result>;
createBySchedule(options:CreateOptions<CronType.SCHEDULE>):Promise<Result>;
createByFrequency(options:CreateOptions<CronType.FREQUENCY>):Promise<Result>;
createByCronExpression(options:CreateOptions<CronType.CronExpression>):Promise<Result>;
list():Promise<Array<CronInfo>>;
start(id:string):Promise<Result>;
startAll():Promise<Result>;
stop(id:string):Promise<Result>;
stopAll():Promise<Result>;
edit(id:string, editData:EditOptions<CronType>);
describe(id:string):Promise<CronInfo>;
delete(id:string):Promise<Result>;
}
- 类型定义
enum CronType{
SCHEDULE, // 一周内某几天中的某几个时间点执行
FREQUENCY, // 一周内某几天中的某几个时间段,按照一定频率重复执行
CronExpression
}
enum CronStatus{
ACTIVE, // 生效中
INACTIVE //未生效,禁用中
}
type CronString = string; // cron 表达式
type CreateOptionsType = CreateOptions<CronType.SCHEDULE> |
CreateOptions<CronType.FREQUENCY> |
CreateOptions<CronType.CronExpression>;
interface Result{
success:boolean;
err?:Error;
data:any;
}
interface CronInfo{
status:CronStatus;
options:CreateOptionsType
}
interface TimeCell{
second: number;
minute: number;
hour: number;
}
- Schedule调用类型
interface CreateOptions<CronType.SCHEDULE>{
name?: string;
type:CronType.SCHEDULE;
scheduleRules:Array<ScheduleRule>;
callback:any=>void;
callbackParams:any;
}
interface ScheduleRule{
scheduleTime: TimeCell | string;
weekdays?:number; // 7位2进制的10进制表示
}
- Frequency调用类型
interface CreateOptions<CronType.FREQUENCY>{
name?: string;
type:CronType.FREQUENCY;
frequencyRules:Array<FrequencyRule>;
callback:any=>void;
callbackParams:any;
}
interface FrequencyRule{
startTime?:TimeCell | string; // default 00:00
endTime?:TimeCell; // default 23:59
frequency?:number; // 以秒为单位 default 60, means every 60 seconds
weekdays?:number; // 7位2进制的10进制表示
}
- CronExpression调用类型
interface CreateOptions<CronType.CronExpression>{
name?: string;
type:CronType.CronExpression;
cronExpressions:Array<string>;
callback:any=>void;
callbackParams:any;
}
接口描述
create
create(options:CreateOptionsType)=>Promise<Result>;
根据传入参数不同,创建一个对应的cron任务写入到数据库,默认状态为DISABLED, 等宿主调用startAll()或者start()函数时,会变为ENABLE状态并生成对应的cronjob正式启动。
例如:
// 周一到周六,每天12:00以及13:05都会执行一次callback,附带参数{}
create({
name:"test1",
type:CronType.SCHEDULE,
scheduleRules:[{hour:12, weekdays:127}, {hour:13, minute:5, weekdays:127}],
callback:(data)=>console.log("fired"),
callbackParams:{},
})
// 每天每小时执行一次callback,附带参数{}
create({
name:"test2",
type:CronType.FREQUENCY,
frequencyRules:[{frequency:60*60}],
callback:data=>console.log("fired"),
callbackParams:{},
})
startAll
startAll():Promise<Result>;
启动数据库中所有存储的cron,生成对应的cronjob。一般在服务器启动时调用
start
start(id:string):Promise<Result>;
单独启动某一cron
list
list():Promise<Array<CronInfo>>;
列出当前数据库中所有的cron信息
describe
describe(id:string):Promise<CronInfo>;
单独列出某一个cron相关信息
delete
delete(id:string):Promise<Result>;
删除某一特定cron,如果该cron已生成了对应的cronjob并正在运行,也会删除对应cronjob
stop
stop(id:string):Promise<Result>;
将数据库中某一cron状态设置为DISABLED状态,但不删除,并如果该cron生成了对应的cronjob,会删除对应的cronjob
数据库设计
id: number cronId: string cronType: CronType status: CronStatus scheduleRule: {} frequencyRule: {} cronExpression: string callback: string callbackParams: {} createdAt: date updatedAt: date
补充
- cron任务集群解决方案收集:
- 通过env设置的方式,使cron服务只在一台服务器上生效
- 优点:处理方便
- 缺点:不优雅、如果该主机宕机,cron服务则直接崩溃
- 通过外置的redis设置交互锁
- 优点:保证了cron的集群部署,保证各集群主机环境统一
- 缺点:额外的redis依赖,额外的交互锁逻辑代码
- 通过env设置的方式,使cron服务只在一台服务器上生效
- 思考:
- 在Service模式的情况下,如何才能让宿主注册回调函数并且不丢失运行上下文?