@sambodhi
2017-01-04T08:26:24.000000Z
字数 3902
阅读 3566
Netflix内容平台工程团队运行由微服务上执行的任务的异步编排驱动的多个业务流程。其中一些是长期运行的流程,跨越几天。这些流程在准备好标题流式传输给全球的观众上发挥关键作用。
这些流程的几个实例是:
传统上,这些流程中的一些已经以ad-hoc方式使用pub/sub的组合来编排,进行直接REST调用,并使用数据库来管理状态。然而,随着微服务数量的增长和进程的复杂性增加,在没有中央编排器的情况下,获得对这些分布式工作流的可见性变得困难。
我们将Conductor构建为一个编排引擎,以满足以下要求,取出在应用程序中需要的样板,并提供一个反应流:
Conductor是为满足上述需求而开发的,并且在Netflix已经使用了将近一年。到目前为止,它已经帮助编排超过260万个流程流,从简单的线性工作流到运行多天的非常复杂的动态工作流。
今天,我们为广泛的社区开源了 Conductor,希望从有类似需求的人那里学习,并增强其能力。你可以在这里找到Conductor的开发文档。
随着点对点任务编排,我们发现越来越多的业务需求和复杂性难以扩展。Pub/sub模型工作的最简单的流程,但很快强调了与该方法相关的一些问题:
在微服务世界中,许多业务流程自动化是通过跨服务编排来驱动的。Conductor可以跨服务实现编排,同时提供对其交互的控制和可见性。具有编排跨微服务的能力也帮助我们利用现有服务来构建新流或更新现有流,以便非常迅速地使用Conductor,有效地提供了一个更容易的途径。
引擎的核心是状态机服务,也称为决策者服务。当工作流事件发生时(例如任务完成、失败等),决策器将工作流蓝图与工作流的当前状态组合,识别下一状态,并编排适当的任务和/或更新工作流的状态。
决策者使用分布式队列来管理预定任务。 我们一直在Dynomite上使用动态队列来管理分布式延迟队列。队列配方是今年早些时候开源的,这里是博客文章。
由工作应用程序实现的任务通过API层进行通信。工作程序通过可由编排引擎调用的REST端点或者通过实现定期检查挂起任务的轮询循环来实现这一点。工作程序旨在实现幂等的无状态功能。轮询模型允许我们处理工作程序的背压,并在可能时基于队列深度提供自动可伸缩性。Conductor提供API来检查每个工作负载的大小,可用于自动编排工作程序实例。
工作程序与引擎的通信
API通过HTTP公开:允许使用HTTP轻松集成不同的客户端。然而,添加另一个协议(例如gRPC)应该是可能的,而且也相对直接。
我们使用Dynomite“作为存储引擎”以及Elasticsearch用于索引执行流。存储API是可插入的,适用于各种存储系统,包括传统的RDBMS或Apache Cassandra,如NoSQL存储。
工作流是使用基于JSON的DSL来定义。工作流蓝图定义了需要执行的一系列任务。每个任务是控制任务(例如:fork、join、decision、sub workflow等)或工作者任务。工作流定义是版本控制的,提供了管理升级和迁移的灵活性。
一个工作流定义的概要:
{
"name": "workflow_name",
"description": "Description of workflow",
"version": 1,
"tasks": [
{
"name": "name_of_task",
"taskReferenceName": "ref_name_unique_within_blueprint",
"inputParameters": {
"movieId": "${workflow.input.movieId}",
"url": "${workflow.input.fileLocation}"
},
"type": "SIMPLE",
... (any other task specific parameters)
},
{}
...
],
"outputParameters": {
"encoded_url": "${encode.output.location}"
}
}
每个任务的行为由其称为任务定义的模板控制。任务定义为每个任务提供控制参数,例如超时、重试策略等。任务可以是由应用程序实现的工作任务或由编排服务器执行的系统任务。Conductor提供开箱即用的系统任务,如Decision、Fork、Join、Sub Workflows和允许插入自定义系统任务的SPI。我们添加了对HTTP任务的支持,以便于调用REST服务。
一个任务定义的JSON代码段:
{
"name": "encode_task",
"retryCount": 3,
"timeoutSeconds": 1200,
"inputKeys": [
"sourceRequestId",
"qcElementType"
],
"outputKeys": [
"state",
"skipped",
"result"
],
"timeoutPolicy": "TIME_OUT_WF",
"retryLogic": "FIXED",
"retryDelaySeconds": 600,
"responseTimeoutSeconds": 3600
}
任务的输入是具有作为工作流实例化或一些其他任务的输出的一部分的输入的映射。这种配置允许将来自工作流或其他任务的输入/输出作为输入路由到可以对其进行操作的任务。例如,可以将编码任务的输出提供给发布任务作为部署到CDN的输入。
用于定义任务输入的JSON代码段:
{
"name": "name_of_task",
"taskReferenceName": "ref_name_unique_within_blueprint",
"inputParameters": {
"movieId": "${workflow.input.movieId}",
"url": "${workflow.input.fileLocation}"
},
"type": "SIMPLE"
}
让我们看看一个非常简单的编码和部署工作流:
总共有3个工作任务和一个控制任务(错误)涉及到:
这三个任务由不同的工作程序实现,这些工作程序使用任务API轮询待决任务。这些是理想状态的幂等任务,它们对给予任务的输入进行操作,执行工作,并且将状态更新回来。
每个任务完成后,决策程序根据蓝图评估工作流实例的状态(对应于工作流实例的版本)并标识要编排的下一组任务,或者如果所有任务完成,则完成工作流。
UI是监控和故障排除工作流程执行的主要机制。UI通过允许基于各种参数的搜索来提供对过程的非常需要的可见性,包括输入/输出参数,并提供了蓝图的可视化呈现及其所采取的路径,以更好地了解流程执行。对于每个工作流实例,UI提供每个任务执行的详细信息,具有以下详细信息:
这里是一个来自厨房水槽工作流程的UI片段,用于生成性能数据:
我们从使用AWS的简单工作流程的早期版本开始。然而,我们选择构建Conductor给出了SWF的一些限制:
最近发布的AWS Step Functions添加了我们在编排引擎中寻求的一些功能。Conductor有可能采用Amazon States Language来定义工作流程。
下面是一些我们已经运行了一年多的生产实例的统计数据。这些工作流中的大多数被内容平台工程用于支持用于内容获取、摄取和编码的各种流。
Total Instances created YTD | 2.6 Million |
No. of distinct workflow definitions | 100 |
No. of unique workers | 190 |
Avg no. of tasks per workflow definition | 6 |
Largest Workflow | 48 tasks |