梁老师小课堂|谈谈分布式任务调度

2020-12-08 17:01:32 浏览数 (1)

我们常说的定时任务有两种架构,一种是本地定时任务调度,另外一种是分布式的。前者将任务参数硬编码在代码配置中,通常还和业务代码混合在一起,部署时通过环境变量来区分。后者通过控制台动态管理任务配置,不需要重启服务,就可以调整执行参数和频率,还可以进行任务的启动、暂停和停止。

我觉得,分布式任务调度这种架构相比本地架构,具有更高的灵活性。接下来我举例说明一下,方便你更直观地感受他们的差别。

假设,有一个定时任务需要处理的数据量非常大,业务又希望能按时按质输出结果,单机负载很有可能成为约束条件,这时,我们很容易想到纵向扩展,添加机器形成一个集群。

在本地定时任务调度架构中,一般做法是,提前预估好分段然后填写到启动参数中。服务启动后,如果发现执行器的CPU利用率等负载过高,就重新调整参数,再重启服务,直到满足执行时间和服务负载的双重要求。

相反,在分布式调度架构中,我们可以使用广播的模式轻松达到要求。这种模式下,调度器会触发集群中所有执行器都执行一次任务,执行器则根据参数处理各自的任务。比如通过参数取模,将整体任务进行平分。当我们发现执行器的CPU利用率等负载过高时,只需要在控制台上点击暂停任务,系统就会发送请求尝试中断执行器的线程,接着我们调整参数,然后重新启动任务就可以了,整个过程都不需要忍受漫长的服务发布时间。

通过这个分片广播的例子,我想说明的是,分布式任务调度这种架构,它的分发处理具备很强的灵活性。那要实现这种架构,得考虑哪些问题呢?

首先,我们得保证定时任务能像夸父逐日那样,永远不停止。

按照惯例,还是举例说明一下。

假设,有这么一个异步补偿任务。它定时扫描最近转码中的视频列表,依靠视频接口进行下一步处理。视频接口返回转码成功则将视频上线,接口返回异常的话,就只更新数据库中相应行记录的更新时间,让其被扫描时机延后。

这种场景不需要也不能同时在多台机器上执行,你想,多台机器同时执行这个任务,数据是交叉污染的,出现冲突和不一致的概率是相对高的。如果采用本地调度这种方案来实现,这台机器在执行任务过程中,负载压力过大,服务就基本无法响应了,下一个周期的调度就无从谈起,待处理的数据将持续积压,业务就受损了。

相反,在分布式调度架构中,系统可以发送远程调用,对集群中各个机器的忙碌状态进行探测,当没有任务在运行或者等待队列为空时,才选定作为执行器,从而分散了各个执行器的压力。

刚刚讨论的是两种架构在出现异常时,功能和行为的差异性。说到差异性,我觉得,两者的物理拓扑结构最值得留意。

在本地定时任务调度架构中,调度器和执行器是不分开的,而在分布式架构中,调度器和执行器归属于不同的系统。那么,问题就来了,当调度器与执行器交互有问题,是否还能保证任务不会被重复执行呢?

要做到这一点,首先要保证的是,任何时候只能有一个调度器在分配任务,实现方式可以是简单粗暴的数据库行锁,进行排它性。还有,调度器发起远程调用触发任务执行后,如果获取响应超时,不能直接转移到其他执行器进行尝试。因为,网络并不总是可靠的,网络延迟也不是不存在的。我们需要进一步判断,才能下结论。比如,当任务调度记录停留在获取响应超时这个阶段超过10分钟,并且注册中心认定这个执行器是失联的,此时才可以丢弃本次结果。

只要能保证不单点,保证任务不会被重复执行,那么一个基本的分布式定时任务调度架构就算完成了。接下来就可以考虑一些优化点了,比如,服务重启导致任务调度错过了触发时间,如何进行处理?再比如,从提高系统稳定性角度考虑,慢任务怎么与快任务进行线程池隔离拆分,避免耗尽调度线程。

好,本文到这里就要结束了。下期见!

0 人点赞