ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Conductor 工作流编排引擎演进:v2.0 架构、任务调度与微服务编排实践

Conductor 工作流编排引擎演进:v2.0 架构、任务调度与微服务编排实践 Conductor 是某海外流媒体公司开发并开源的一款工作流编排引擎主要用于微服务编排、分布式工作流管理和复杂业务流程自动化。如果你是第一次接触 Conductor可以先阅读此前的入门文章和相关文档了解它在微服务编排和分布式工作流可观测性方面的基本能力。自发布以来的两年间Conductor 已被广泛采用并在该公司许多核心业务流程中发挥着关键作用。许多内容工程和工作室工程服务都依赖 Conductor 来高效处理业务流程内部媒体数据库就是其中一个典型例子。在企业研发实践中Conductor 这类系统更偏向底层业务流程与服务编排而研发团队还需要在目标、需求、开发、测试、发布和知识沉淀之间形成管理闭环。pingcode智能化研发管理工具可以覆盖研发全生命周期管理并打通研发工具链让过程数据、质量数据和知识经验在团队之间更顺畅地流转。在本文中我们将介绍 Conductor v2.0 及更高版本的最新进展重点包括部署架构、Cassandra 持久层、gRPC 框架、任务调度、工作流状态监听器、API 验证以及未来路线图并回答一些常见问题同时向社区贡献者表达感谢。Conductor 在微服务架构中的使用方式部署架构Conductor 是内容工程部门使用最广泛的服务之一。它可以与众多模块集成。在当前部署中我们使用 Jersey 服务模块、Cassandra用于持久化执行数据、Dynomite用于持久化元数据、DynoQueues基于 Dynomite 构建的队列机制、Elasticsearch作为辅助数据存储和索引器以及内部指标系统用于指标管理。我们的集群通常由 12 到 18 个某云平台 m4.4xlarge 实例组成日常运行时大约使用 30% 的容量。需要说明的是Cassandra 持久化模块目前只是部分实现。在内部我们并没有维护 Conductor 的独立分支版本。相反我们使用了一个封装层它会引入最新版本的 Conductor并在部署前添加内部基础设施组件和库。这样一来我们既可以持续将变更推送到开源版本又能确保这些变更功能完整并经过充分测试。采用情况截至本文撰写时Conductor 已经为该公司 50 多个团队管理着 600 多个工作流定义。虽然我们目前还没有主动测量第 n 百分位延迟等指标但现有生产工作负载已经足以证明 Conductor 的性能和稳定性。在一个典型的 7 天周期内Conductor 会处理大量工作流执行请求。内部仪表盘可以直观展示这些工作流执行指标帮助团队观察系统的使用情况和运行状态。典型应用场景Conductor 在内部的一些典型应用场景可以归纳为以下几类内容摄取与分发内容质量控制内容本地化编码与部署IMF互操作母版格式交付营销技术工作室工程Conductor v2.0 的核心更新gRPC 框架v2.0 的关键特性之一是引入了 gRPC 框架作为 REST 的替代方案或补充方案。这项能力由某代码托管平台上的开源社区贡献者参与实现也进一步体现了社区贡献对 Conductor 发展的重要价值。Cassandra 持久层为了实现数据存储的水平扩展并应对大量并发工作流执行例如每天数百万级别的工作流我们选择使用 Cassandra 来提供弹性扩展能力并满足高吞吐需求。外部有效载荷存储我们实现了外部有效载荷存储目的是防止 Conductor 被当作数据持久化系统使用同时减轻后端数据存储的压力。在工作流系统中任务输入输出可能包含较大的数据对象。如果这些数据全部直接存储在 Conductor 的核心存储中系统的可维护性和扩展性都会受到影响。外部有效载荷存储可以将大体积数据转移到外部存储系统中而 Conductor 只保留必要引用从而让编排引擎专注于工作流执行本身。动态工作流执行有些场景需要执行大量不同的工作流定义甚至是任意数量的工作流定义还有一些场景只是为了测试、分析或一次性任务而运行临时工作流。如果每次都要先将工作流定义注册到元数据存储中再执行一次工作流就会带来不必要的开销。动态创建和执行工作流的能力消除了这一障碍。通过这项能力用户可以在运行时创建并执行工作流而不必提前将所有定义注册到系统中。这也是我们与开源社区协作取得的又一项重要成果。工作流状态监听器Conductor 可以配置为在工作流完成或终止时向外部系统或队列发布通知。工作流状态监听器提供了一组钩子可以连接到你选择的任何通知系统。社区贡献了一个实现方案可以根据工作流状态向 dyno 队列发布消息。随后可以在这些队列上配置事件处理器在工作流终止时触发特定工作流或任务完成后续操作。批量工作流管理从可运维性的角度来看工作流层面的批量操作一直都非常必要。当系统规模扩大后下游依赖异常、工作进程故障或任务执行错误都可能导致大量任务失败。此时能够对工作流执行批量操作就显得尤为重要。批量 API 让运维人员可以从更宏观的层面对系统中正在执行的工作流进行控制。将 Elasticsearch 与持久化机制解耦过去索引层和持久化层之间存在一定耦合。我们通过将索引层迁移到独立的持久化模块并暴露workflow.elasticsearch.instanceType属性来选择索引引擎类型消除了这种相互依赖关系。此外我们还通过ExecutionDAOFacade将编排逻辑从主持久化层上移到服务层从而进一步解耦索引器和持久化层。支持 Elasticsearch 5/6作为 v2.x 版本主要升级的一部分Conductor 新增了对 Elasticsearch 5 和 6 的支持。本次升级还提供了使用 Elasticsearch RestClient 的选项而不再强制依赖早期版本中的 Transport Client。这使得在 Conductor 部署中使用托管 Elasticsearch 集群成为可能。任务速率限制与并发执行限制任务速率限制可以帮助实现任务调度层面的限流。任务定义中的rateLimitFrequencyInSeconds参数用于设置任务执行的时间窗口rateLimitPerFrequency则定义了在该时间窗口内可以调度的任务数量。另一方面concurrentExecLimit用于限制任务并发执行数量。也就是说在任意给定时间点已调度任务的总数都会低于concurrentExecLimit。这些参数可以组合使用以实现所需的节流和速率限制策略。API 验证验证能力是 Conductor 1.x 中缺失的一项核心能力。为了提升易用性和可运维性我们在新版本中增加了验证功能。实践证明这项能力极大地帮助用户在创建工作流定义和任务定义时发现错误。验证机制会要求用户先创建并注册任务定义然后才能注册使用这些任务的工作流定义。它还会确保工作流定义格式正确并检查工作流中各个任务的输入输出连接是否合理。如果系统发现异常会向用户返回详细错误信息说明失败原因帮助用户更快定位问题。开发者实验室、日志与指标我们持续改进日志记录和指标体系并同步更新文档以反映 Conductor 的最新状态。为了提供更流畅的入门体验我们创建了开发者实验室用于指导用户创建任务和工作流定义、管理工作流生命周期、配置带有事件的高级工作流等。同时开发者实验室也会简要介绍 Conductor API、用户界面和其他模块。Conductor 新增任务类型系统任务已经被证明在定义工作流结构和控制流程方面非常有价值。因此Conductor 2.x 为系统任务新增了多项能力其中大部分来自社区贡献。Lambda 任务Lambda 任务会在工作流运行时使用 Nashorn JavaScript 求值引擎执行临时逻辑。它允许用户通过简单的 JavaScript 表达式直接完成轻量级求值操作而不必为了简单判断专门创建一个工作进程。Terminate 任务当工作流逻辑需要以特定输出终止时Terminate 任务非常有用。例如如果某个决策任务的评估结果为假而我们不希望继续执行工作流中的剩余任务那么相比创建一个包含任务列表的决策任务甚至创建一个空任务列表使用 Terminate 任务来限定决策范围并终止工作流执行会更加清晰。Exclusive Join 任务Exclusive Join 任务用于捕获决策流程中的任务输出。它适用于将某个决策分支中的任务输出连接到后续任务输入的场景。这些数据只在工作流执行期间可用而 Exclusive Join 任务可以用来收集任意决策分支中某个任务的输出。有关新增功能的详细实现细节可参阅相关文档。Conductor 工作流编排的未来路线图我们希望继续为 Conductor 增加更多功能和改进。下面这份清单可以视为一份长期路线图。它并不完整我们也非常欢迎社区提出新的想法和建议。以下项目排名不分先后。高级事件管理事件聚合与分发目前事件的生成和处理机制相对简单一个事件任务只能创建一条消息一个任务也只能等待一个事件。我们设想引入事件聚合和分发机制使 Conductor 能够覆盖更多场景。一个初步设想是允许一个任务等待多个事件也允许一个事件推动多个任务继续执行。用户界面改进当前 UI 已经提供了一种简洁方式用于可视化和跟踪工作流执行情况。但我们仍希望通过以下能力进一步增强它在用户界面中创建元数据对象支持直接启动工作流可视化执行指标提供展示异常值的管理员仪表盘新增任务类型Goto、Loop 等Conductor 一直使用有向无环图DAG结构来定义工作流。Goto 和 Loop 任务确实对应一些有效的业务场景但它们会偏离 DAG 结构。我们希望在不破坏现有工作流执行规则的前提下增加对这些任务类型的支持。这将解锁更多用例例如将数据流式传输到任务或者在工作流中重复执行一组任务。支持可复用的通用任务我们同样看到了共享可复用任务的价值。这类任务可以执行特定操作并被不同团队直接复用。在内部部署 Conductor 时我们会添加一些特定于内部服务的任务用户可以直接使用这些任务而不必从头开始重新创建。例如我们提供了一个容器启动任务使用户可以在工作流执行过程中启动新的容器。我们希望进一步扩展这一思路让 Conductor 能够提供一个通用任务库。基于推送的任务调度接口当前 Conductor 架构基于工作节点轮询机制由工作节点主动拉取待执行任务。我们希望增强 gRPC 模块利用双向通道在任务调度完成后立即将任务推送给工作节点。这样可以减少网络流量、降低服务器负载并避免冗余的客户端调用。验证任务输入键和输出键这项能力旨在为任务提供类型安全保障并为任务定义提供一个参数化接口。这样一来任务一旦注册就可以在 Conductor 中被完整复用。它也会形成一种契约让用户能够浏览可用任务定义并在自己的工作流中使用这些任务即使这些任务由其他团队或用户实现。该功能还会涉及用户界面的增强以便展示这类契约信息。在 Cassandra 中实现 MetadataDAO如前所述Cassandra 模块目前仅提供了工作流执行持久化的部分实现尚未实现元数据持久化能力。我们正在努力尽快补齐这一功能。任务完成时的可插拔通知类似于工作流状态监听器我们希望提供一个可扩展接口用于在任务执行完成时发送通知。发布官方 Python 客户端我们看到 Python 客户端在社区中得到了广泛使用。然而目前包管理平台上还没有官方 Python 客户端而且现有客户端也缺少 Java 客户端中的一些新增功能。我们希望实现功能上的完全一致并从 Conductor 的代码库中发布客户端随后实现客户端到包管理平台的自动化发布流程。从关键路径中移除 ElasticsearchElasticsearch 在 Conductor 中非常有用但我们希望对于尚未配置 Elasticsearch 的用户而言它应当是一个可选组件。这意味着我们需要将 Elasticsearch 从工作流关键执行路径中移除使其作为可选层存在。可插拔身份认证与授权Conductor 目前不支持 API 或 UI 层面的身份认证和授权。我们认为这将带来很大价值也是社区中经常被提出的需求。验证与测试“空运行”能力也就是在不真正通过工作进程和全部相关设置执行工作流的情况下对工作流定义进行评估可以让执行路径的测试和调试变得更加容易。Conductor 开源社区参与如果你希望成为 Conductor 社区的一员并为路线图中的某个项目或者你认为能够带来显著价值的项目做出贡献可以阅读贡献指南获取说明也可以在用户论坛中发起讨论。我们也强烈鼓励大家对基于 Conductor 构建的自定义功能进行完善、通用化并与社区分享。我们非常感谢社区的积极参与也为社区为 Conductor 做出的重要贡献感到自豪。我们希望在此基础上继续前进在强大社区的支持下让 Conductor 被更广泛地采用。对于这类长期演进的开源项目而言除了代码贡献和架构设计任务分配、路线图管理、文档协同和跨角色沟通同样重要。通用项目协作系统可以承接任务、项目、文档、目标、日历和审批等协作场景帮助不同成员围绕同一项目目标高效配合。Conductor 由某海外技术团队的媒体工作流基础设施团队维护。如果你喜欢构建分布式系统带来的挑战并对大规模内容与工作室工程生态感兴趣欢迎与相关团队取得联系。总体来看Conductor v2.0 及后续版本的演进反映了工作流编排引擎在微服务架构中的关键价值它不仅要负责复杂业务流程的自动化执行还要在任务调度、数据持久化、事件管理、可观测性、扩展性和社区生态之间取得平衡。对于需要管理大规模分布式工作流的团队而言这类工作流编排实践具有很高的参考价值。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进