问题
- 在正常的业务集群中植入定时任务功能,需要额外的开发来保证任务不会重复执行
- 有些服务是每隔一段时间查询需要执行的定时任务列表,然后一个个执行,如果一个时间段内定时任务的数量特别大,对服务也会带来一定的压力
- 任务的执行每个时间段都只有单个机器能够执行,无法使用到整个集群的资源
优化方案
- 构建一个时间轮,按照固定的间隔将时间轮划分为多个时间区间,每个时间区间对应一个消息队列,任务加入时,通过计算其触发时间,将其加入对应区间的消息队列
- 如果指针指向的时间区间没有对应的消息队列,则不做任何执行,等待指针指向下一个区间
- 根据业务需要和对数据的细分程度的容忍,来制定对应的时间间隔(比如1分钟),将时间轮划分成不同的时间区间,同属于一个时间区间的任务,将同一批执行。
- 集群中的所有节点可以各自维护一个时间轮,当指针指向对应的区间时,即消费消息队列中的数据,使得集群能够分担整个任务执行的压力(集群中的各个节点服务器时间尽量一致)
- 任务不会重复执行,消息队列中的任务数据被单个节点执行成功之后自动弹出。
- 在执行任务时,该方案仍然无法将单个任务分发到集群中的所有节点执行
type PreSliceTask interface {
//任务实现,允许将一个任务分片并发执行
Slice() []interface{}
//任务实现,判断该任务分片是否是主分片,主分片负责在执行成功后计算下次待执行的时间
IsMain() bool
//任务实现,计算下次执行的时间,如果为0,则不继续投入时间区间
Next() time.Time
}
- 任务对象实现上述伪代码接口,用以支持任务的预处理,比如每隔一个时间段自动给用户发通知,预处理可以查询用户集合并分片,用来分担执行压力
- 允许单个任务判断是否可以分片,以提高单个任务并发执行的效率,但只能定义一个主分片,主分片负责计算该任务下次执行的时间,如果为0,则不继续投入时间区间。