likes
comments
collection
share

几种简单的任务调度方案实现

作者站长头像
站长
· 阅读数 8

场景

我们在实际开发过程中,有没有遇到这样的场景:用户会提交一些耗时且资源占用较高的任务,比如音视频处理、数据计算、数据处理等,这些任务都需要大量的计算资源。为了解决这个问题,一般服务端会存在多个实例,组成一个集群,来运行这些任务。那么,我们应该如何有效地调度这些任务呢?下面我们考虑几种比较简单且实际的实现方式。

设计目标

什么是设计目标? 我们的方案需要满足什么样的目的,所有的设计都需要围绕着我们的目的进行。

一定要满足的

  • 弹性扩展,任务处理集群需要可以根据业务实际需要进行弹性扩缩容
  • 高可用性,任务处理集群需要采用高可用的架构,保证即使某个实例出现故障也能够保证任务的顺利处理
  • 负载均衡,能够合理的来分配任务,保证每个实例的负载均衡,减少任务等待时间

尽量满足,或后续可以扩展的

  • 任务监控,我们需要对任务的执行情况进行监控,及时发现任务的异常情况,并采取相应的处理措施
  • 任务调度,我们需要考虑如何合理地调度任务,使得任务的执行时间最短,效率最高
  • 任务优先级,我们需要考虑不同任务的优先级,优先处理一些紧急任务,避免长时间等待

我们在设计一个服务时,有没有从纯技术的角度来思考我们的设计目标?不仅仅是满足业务需求,还要考虑到设计的可维护性、可扩展性、可复用性等方面。因为我们所设计的服务不仅仅是为了解决当下的业务需求,更是为了未来的发展和变化预留余地。因此,我们需要合理地选取技术架构,采用适当的设计模式,规范代码编写规范,以便于未来的维护和升级。另外,我们还需要充分考虑服务的性能、安全性、可靠性等因素,确保服务能够稳定地运行,并能够及时地发现和处理问题。

三种可以实现的方案

一、消息队列

将任务提交到消息队列,然后处理集群注册为消费者

几种简单的任务调度方案实现

优点:

  • 消息队列本身有机制可以保证消费顺序和消息的持久化等,实现较容易
  • 可以动态扩缩容,没有单点问题

缺点:

  • 引入了额外的中间件,提升了架构复杂度
  • 为了资源分配均匀,拉取消息和确认消息需要手动处理

实现时还要考虑很多问题,比如消费的幂等性处理;任务的状态更新与确认消息的原子性怎么保障,等等。

二、任务协调者

增加一个任务协调者的角色,它维护着所有可用节点,并且负责任务的分配.

几种简单的任务调度方案实现

优点:

  • 由于所有实例都由协调者维护,意味着我们可以非常灵活的控制实例的调度,并且很容易的实现各种监控看板

缺点:

  • 实现复杂,包括协调者的实现,以及对应的SDK实现。

这个方案实现起来就更复杂了,协调者的高可用怎么保障;节点与协调者之间如何互相感知,节点掉线怎么办;负载均衡策略如何设计,等等。

三、定时任务

定时任务,集群内每个实例都通过定时任务扫描任务表,自行拉取任务处理。

几种简单的任务调度方案实现

优点:

  • 实现简单,无需额外的中间件或服务支持

缺点:

  • 实时性,定时任务无论如何都会有一定的延迟,无法实时的处理任务
  • 资源浪费,无论当前有没有需执行的任务,都需要频繁的查询DB

这个实现比较简单,但是也是有一些问题需要考虑的,比如怎么保证任务不被多个节点同时获取到;频繁的DB查询怎么提高性能;定时任务的时间间隔怎么设置,等等。

总结

整体来说每种方案优缺点明显,这里只是简单举例了几种比较容易实现的方式,但是在实际上设计或开发的时候,还有大量的细节需要考虑,比如:

  1. 怎么判断自己是否还有足够的资源执行任务?是根据cpu、内存? 还是固定每个节点的有任务上限数?
  2. 任务需不需有顺序? 如果需要有顺序怎么实现?
  3. 任务失败了怎么办?重试还是放弃?
  4. 监控怎么做? 监控的目的是什么? 需要监控哪些指标?业务指标还是性能指标?
  5. ...等等

这里只是抛砖引玉,在实际的业务场景中,我们可以根据业务需求及目标来设计合适的方案,没有哪种方案是可以万能通用的,大部分的架构设计都是围绕着实际业务来进行的。 PS: 如果大家有什么想法可以评论区讨论