简介:本文介绍了异步任务处理系统是如何解决业务长耗时、高并发难题的。
作者:不瞋 (阿里云 Serverless 技术负责人)
当我们构建一个应用,总是希望它是响应迅速,成本低廉的。而在实际中,我们的系统却面临各种各样的挑战,例如不可预测的流量高峰,依赖的下游服务变得缓慢,少量请求却消耗大量 CPU/内存资源。这些因素常常导致整个系统被拖慢,甚至不能响应请求。为了让应用服务总是响应迅速,很多时候不得不预留更多的计算资源,但大部分时候,这些计算资源都是闲置的。一种更好的做法是将耗时缓慢,或者需要消耗大量资源的处理逻辑从请求处理主逻辑中剥离出来,交给更具资源弹性的系统异步执行,不但让请求能够被迅速处理返回给用户,也节省了成本。
一般来说,长耗时,消耗大量资源,或者容易出错的逻辑,非常适合从请求主流程中剥离出来,异步执行。例如新用户注册,注册成功后,系统通常会发送一封欢迎邮件。发送欢迎邮件的动作就可以从注册流程中剥离出来。另一个例子是用户上传图片,图片上传后通常需要生成不同大小的缩略图。但图片处理的过程不必包含在图片上传处理流程中,用户上传图片成功后就可以结束流程,生成缩略图等处理逻辑可以作为异步任务执行。这样应用服务器避免被图片处理等计算密集型任务压垮,用户也能更快的得到响应。常见的异步执行任务包括:
Slack,Pinterest,Facebook 等公司都广泛的使用异步任务,实现更好的服务可用性,更低的成本。根据 Dropbox 统计,他们的业务场景中一共有超过100种不同类型的异步任务。一个功能完备的异步任务处理系统能带来显著的收益:
任务处理系统通常包括三部分:任务 API 和可观测,任务分发和任务执行。我们首先介绍这三个子系统的功能,然后再讨论整个系统面临的技术挑战和解决方案。
该子系统提供一组任务相关的 API,包括任务创建、查询、删除等等。用户通过 GUI,命令行工具,后者直接调用 API 的方式使用系统功能。以 Dashboard 等方式呈现的可观测能力也非常重要。好的任务处理系统应当包括以下可观测能力:
任务分发负责任务的调度分发。一个能应用于生产环境的任务分发系统通常要具备以下功能:
任务分发的架构可分为拉模式和推模式。拉模式通过任务队列分发任务。执行任务的实例主动从任务队列中拉取任务,处理完毕后再拉取新任务。相对于拉模式,推模式增加了一个分配器的角色。分配器从任务队列中读取任务,进行调度,推送给合适的任务执行实例。
拉模式的架构清晰,基于 Redis 等流行软件可以快速搭建任务分发系统,在简单任务场景下表现良好。但如果要支持任务去重,任务优先级,批量暂停或删除,弹性的资源扩缩容等复杂业务场景需要的功能,拉模式的实现复杂度会迅速增加。实践中,拉模式面临以下一些主要的挑战:
推模式的核心思想是将任务队列和任务执行实例解耦,平台侧和用户的边界更加清晰。用户只需要专注于任务处理逻辑的实现,而任务队列,任务执行节点资源池的管理都由平台负责。推模式的解耦也让任务执行节点的扩容不再受任务队列的连接资源等方面的限制,能够实现更高的弹性。但推模式也引入了很多的复杂度,任务的优先级管理,负载均衡,调度分发,流控等都由分配器负责,分配器需要和上下游系统联动。
总的来说,当任务场景变得复杂后,无论拉还是推模式,系统复杂度都不低。但推模式让平台和用户的边界更清晰,简化了用户的使用复杂度,因此有较强技术实力的团队,实现平台级的任务处理系统时,通常会选择推模式。
任务执行子系统管理一批执行任务的 worker 节点,以弹性、可靠的方式执行任务。典型的任务执行子系统需具备如下功能:
任务执行子系统通常使用 K8s 管理的容器集群作为资源池。K8s 能够管理节点,将执行任务的容器实例调度到合适的节点上。K8s 也内置了作业(Jobs)和定时作业(Cron Jobs)的支持,简化了用户使用 Job 负载的难度。K8s 有助于实现共享资源池管理,任务资源隔离等功能。但 K8s 主要能力还是在POD/实例管理上,很多时候需要开发更多的功能来满足异步任务场景的需求。例如:
注意:K8s 中的作业(Job)和本文讨论的任务(task)有一些区别。K8s 的 Job 通常包含处理一个或者多个任务。本文的任务是一个原子的概念,单个任务只在一个实例上执行。执行时长从几十毫秒到数小时不等。
根据前述对异步任务处理系统的架构和功能的分析,我们将异步任务处理系统的能力分为以下三层:
Level 1 | Level 2 | Level 3 | |
任务的可靠分发 | 支持 | 支持 | 支持 |
任务定时/延时发送 | 取决于选择的消息队列能力。一般支持定时任务,但不支持延时任务 | 支持 | 支持 |
任务去重 | 不支持 | 支持 | 支持 |
任务错误自动重试 | 有限支持。一般依赖于 K8s Jobs 内置的重试策略。对于未使用 K8s Jobs 的任务,则需用户在任务处理逻辑中自行实现 | 有限支持。一般依赖于 K8s Jobs 内置的重试策略。对于未使用 K8s Jobs 的任务,则需用户在任务处理逻辑中自行实现 | 支持。平台和用户界限清晰,根据用户设定的策略重试 |
任务负载均衡 | 有限支持。在任务执行实例规模小的情况下通过消息队列实现 | 有限支持。在任务执行实例规模小的情况下通过消息队列实现 | 支持。系统具备大规模节点的负载均衡能力 |
任务优先级 | 不支持 | 有限支持。允许用户为高优先级任务预留资源,或者限制低优先级任务的资源使用 | 支持。高优先级任务可抢占低优先级任务资源,同时系统会兼顾公平,避免低优先级任务被饿死 |
任务流控 | 不支持 | 不支持。一般是为不同任务类型或者业务方配置独立的队列和计算资源 | 在系统的每个环节具备流控能力,系统不会因为任务爆发式提交雪崩 |
任务批量暂停/删除 | 不支持 | 有限支持。取决于是否为不同任务类型或者业务方配置独立的队列和计算资源 | 支持 |
共享资源池 | 有限支持。依赖 K8s 的调度能力。一般是为各个业务方搭建不同的集群 | 有限支持。依赖 K8s 的调度能力。一般是为各个业务方搭建不同的集群 | 支持。不同类型的任务,不同业务场景共享同一个资源池 |
资源弹性伸缩 | 不支持。K8s 的 HPA 通常难以满足任务场景下的伸缩要求 | 不支持。K8s 的 HPA 通常难以满足任务场景下的伸缩要求 | 支持。根据排队任务数,节点资源利用率等多维度实时伸缩 |
任务资源隔离 | 支持。依赖容器的资源隔离能力 | 支持。依赖容器的资源隔离能力 | 支持。依赖容器的资源隔离能力 |
任务资源配额 | 不支持 | 支持 | 支持 |
简化任务处理逻辑编码 | 不支持。任务处理逻辑需要自行拉取任务,执行任务 | 不支持。任务处理逻辑需要自行拉取任务,执行任务 | 支持 |
系统平滑升级 | 不支持 | 不支持 | 支持 |
执行结果通知 | 不支持 | 不支持 | 支持 |
可观测性 | 依赖 K8s,消息队列等开源软件自身的可观测能力。具备基本的任务状态查询 | 依赖 K8s,消息队列等开源软件自身的可观测能力。具备基本的任务状态查询 | 具备从任务到系统各个层面的完整可观测能力 |
异步任务是构建弹性、高可用,响应迅速应用的重要手段。本文对异步任务的适用场景和收益进行了介绍,并讨论了典型异步任务系统的架构、功能和工程实践。要实现一个能够满足多种业务场景需求,弹性可扩展的异步任务处理平台具有较高的复杂度。而阿里云函数计算 FC 为用户提供了开箱即用的,接近于Level ß3能力的异步任务处理服务。用户只需要创建任务处理函数,通过控制台,命令行工具,API/SDK,事件触发等多种方式提交任务,就可以弹性、可靠、可观测完备的方式处理任务。函数计算异步任务覆盖任务处理时长从毫秒到24小时的场景,被阿里云数据库自制服务 DAS,支付宝小程序压测平台,网易云音乐,新东方,分众传媒,米连等集团内外客户广泛应用。
对比项 | 函数计算异步任务 | K8S Jobs |
适用场景 | 适合任务执行时长数十毫秒的实时任务和任务执行时长几十小时的离线任务 | 适合任务提交速度要求不高,任务负载比较固定,任务实时性要求不高的离线任务 |
任务可观测能力 | 支持。提供日志,任务排队数等指标,任务链路耗时,任务状态查询等丰富可观测能力 | 自行整合开源软件实现。 |
任务实例自动扩缩容 | 支持。根据任务排队数,实例资源使用率自动扩缩容 | 不支持。一般通过任务队列,自行实现自动扩缩容和实例负载均衡,复杂度高 |
任务实例伸缩速度 | 毫秒级 | 分钟级 |
任务实例资源利用率 | 用户只需要选择合适的实例规格,实例自动伸缩,按实际处理任务的时长计量,资源利用率高 | 需在作业(Job)提交时确定实例的规格和数目。实例难以自动伸缩和负载均衡,资源利用率低 |
任务提交速度 | 单个用户支持每秒提交数万任务 | 整个集群每秒最多启动数百作业(Jobs) |
任务定时/延时提交 | 支持 | 支持定时任务,不支持延时任务 |
任务去重 | 支持 | 不支持 |
暂停/恢复任务执行 | 支持 | Alpha 状态(K8S v1.21) |
终止指定任务 | 支持 | 有限支持。通过终止任务实例间接实现 |
任务流控 | 支持。可在用户,任务处理函数等不同粒度进行流控 | 不支持 |
任务结果自动回调 | 支持 | 不支持 |
开发运维成本 | 只需要实现任务的处理逻辑 | 需维护K8S集群 |
2、网易云音乐音视频算法的 Serverless 探索之路: https://developer.aliyun.com/article/801501
3、其它异步任务案例:https://developer.aliyun.com/article/815182
参考链接:
[1] slack engineering:https://slack.engineering/scaling-slacks-job-queue/
[2] Facebook:https://engineering.fb.com/2020/08/17/production-engineering/async/
[3] Dropbox 统计:https://dropbox.tech/infrastructure/asynchronous-task-scheduling-at-dropbox
[4] Netflix Cosmos 平台:https://netflixtechblog.com/the-netflix-cosmos-platform-35c14d9351ad
[5] keda:https://keda.sh/
[6] Autoscaling Asynchronous Job Queues :https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/autoscaling-asynchronous-job-queues.pdf
[7] 异步任务:https://help.aliyun.com/document_detail/372531.html
[8] Sample and Hold 算法:https://dl.acm.org/doi/10.1145/633025.633056
更多内容关注 Serverless 微信公众号(ID:serverlessdevs),汇集 Serverless 技术最全内容,定期举办 Serverless 活动、直播,用户最佳实践。
原文链接:https://developer.aliyun.com/article/902613?
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。