场景
我们在实际开发过程中,有没有遇到这样的场景:用户会提交一些耗时且资源占用较高的任务,比如音视频处理、数据计算、数据处理等,这些任务都需要大量的计算资源。为了解决这个问题,一般服务端会存在多个实例,组成一个集群,来运行这些任务。那么,我们应该如何有效地调度这些任务呢?下面我们考虑几种比较简单且实际的实现方式。
设计目标
什么是设计目标? 我们的方案需要满足什么样的目的,所有的设计都需要围绕着我们的目的进行。
一定要满足的
- 弹性扩展,任务处理集群需要可以根据业务实际需要进行弹性扩缩容
- 高可用性,任务处理集群需要采用高可用的架构,保证即使某个实例出现故障也能够保证任务的顺利处理
- 负载均衡,能够合理的来分配任务,保证每个实例的负载均衡,减少任务等待时间
尽量满足,或后续可以扩展的
- 任务监控,我们需要对任务的执行情况进行监控,及时发现任务的异常情况,并采取相应的处理措施
- 任务调度,我们需要考虑如何合理地调度任务,使得任务的执行时间最短,效率最高
- 任务优先级,我们需要考虑不同任务的优先级,优先处理一些紧急任务,避免长时间等待
我们在设计一个服务时,有没有从纯技术的角度来思考我们的设计目标?不仅仅是满足业务需求,还要考虑到设计的可维护性、可扩展性、可复用性等方面。因为我们所设计的服务不仅仅是为了解决当下的业务需求,更是为了未来的发展和变化预留余地。因此,我们需要合理地选取技术架构,采用适当的设计模式,规范代码编写规范,以便于未来的维护和升级。另外,我们还需要充分考虑服务的性能、安全性、可靠性等因素,确保服务能够稳定地运行,并能够及时地发现和处理问题。
三种可以实现的方案
一、消息队列
将任务提交到消息队列,然后处理集群注册为消费者
优点:
- 消息队列本身有机制可以保证消费顺序和消息的持久化等,实现较容易
- 可以动态扩缩容,没有单点问题
缺点:
- 引入了额外的中间件,提升了架构复杂度
- 为了资源分配均匀,拉取消息和确认消息需要手动处理
实现时还要考虑很多问题,比如消费的幂等性处理;任务的状态更新与确认消息的原子性怎么保障,等等。
二、任务协调者
增加一个任务协调者的角色,它维护着所有可用节点,并且负责任务的分配.
优点:
- 由于所有实例都由协调者维护,意味着我们可以非常灵活的控制实例的调度,并且很容易的实现各种监控看板
缺点:
- 实现复杂,包括协调者的实现,以及对应的SDK实现。
这个方案实现起来就更复杂了,协调者的高可用怎么保障;节点与协调者之间如何互相感知,节点掉线怎么办;负载均衡策略如何设计,等等。
三、定时任务
定时任务,集群内每个实例都通过定时任务扫描任务表,自行拉取任务处理。
优点:
- 实现简单,无需额外的中间件或服务支持
缺点:
- 实时性,定时任务无论如何都会有一定的延迟,无法实时的处理任务
- 资源浪费,无论当前有没有需执行的任务,都需要频繁的查询DB
这个实现比较简单,但是也是有一些问题需要考虑的,比如怎么保证任务不被多个节点同时获取到;频繁的DB查询怎么提高性能;定时任务的时间间隔怎么设置,等等。
总结
整体来说每种方案优缺点明显,这里只是简单举例了几种比较容易实现的方式,但是在实际上设计或开发的时候,还有大量的细节需要考虑,比如:
这里只是抛砖引玉,在实际的业务场景中,我们可以根据业务需求及目标来设计合适的方案,没有哪种方案是可以万能通用的,大部分的架构设计都是围绕着实际业务来进行的。
PS: 如果大家有什么想法可以评论区讨论