微服务编排平台设计与实现

5,613 阅读4分钟
服务编排
service orchestration.png
一言以蔽之
  • 运行在云端的工作流编排引擎
工作流
  • 处理一组数据的一系列任务
  • 描述某事物从未完成到完成,或从原始到处理的路径
  • 例子
    camunda example.png
定义
  • 任务(Task):任务是组装工作流的最小的基础单元,工作流中至少有一项任务。任务由执行器(Worker)轮询执行
    task_states.png
  • 工作流(Workflow):即工作流程(业务流程),由若干任务组成,代表了真实的业务需求。如下图所示,每一步为一个任务。
    bgnr_complete_workflow.png
  • 执行器(Worker):Worker是实现了Worker接口的独立进程,它通过轮询任务队列的方式来获取任务,执行完任后,调用管理接口主动推送任务执行结果
概念关系
image.png
工作流与微服务
image.png
姿势
image.png
  • 定义用来组装业务流程的任务
  • 定义由若干任务组装的业务流程
  • 创建任务执行器定期轮询任务
  • 查看流程执行结果
如何工作
image.png
高层架构
  • 架构亮点 - API层与存储层提供了可插拔特性,可以与不同的后端和队列服务提供者一起工作
    conductor-architecture.png
运行时模型
  • Conductor遵循基于RPC的通讯模型
  • Workers 作为远程系统通过HTTP协议与Conductor Servers通信
  • 通过任务队列为Workers调度任务
  • 支持不同的数据库作为存储和索引的实现
    overview.png
系统组件
image.png
特性
  • 分布式服务系统,可以高效的存储工作流状态信息
  • 允许创建流程、业务流程,其中每个单独的任务都可以由相同/不同的微服务实现
  • 具有合理默认值的各种可配置属性,可以微调工作流和任务执行,例如速率限制,并发执行限制等
  • 基于JSON DSL定义的执行流程
  • 高性能,能够扩展到数百万个并发运行的流程
  • 支持同步处理所有任务
  • 支持由客户端抽象的队列服务
  • 支持事件处理,可以通过外部操作来控制工作流
  • 支持多语言的客户端(Java、Python等)
  • 内置丰富的逻辑处理能力(Decision/Sub-Workflow/Fork/Join/Do-While/Wait/Event/Error Handling等)
  • 提供了可观察、可追踪等若干管控能力
目的
定位.png

以现有分布式微服务为基础数据,搭建分布式服务编排平台。目标是现有服务无需特殊改造即能灵活装配出新的业务能力,更好地发挥现有服务的业务价值,减少冗余的服务数量。提供丰富的编排能力,适应客户丰富的业务场景,并保障配置的业务流程能高效运行。除了以上基本功能外,还保证分布式数据的一致性、高并发、跨可用区高可用。

价值

服务编排系统可以帮助我们编排现有的微服务,以提高现有业务的整合能力,极大程度实现服务重用、服务增强,减少了开发成本,从而实现企业的降本增效,以及加速企业新业务研发。

场景
  • 奈飞 - 内容工程
    • 内容提取与传送
    • 内容本地化
    • 内容质量控制
  • 现有业务增强,比如资产交易形态的转变
  • 打通现有孤立业务,比如打通HR/CMS/OA独立的系统功能
  • 根据现有业务编织新业务,比如复用现有的CMS
工程解析
ArtifactDescription
conductor-commonCommon models used by various conductor modules
conductor-coreCore Conductor module
conductor-redis-persistencePersistence and queue using Redis/Dynomite
conductor-cassandra-persistencePersistence using Cassandra
conductor-mysql-persistencePersistence and queue using MySQL
conductor-postgres-persistencePersistence and queue using Postgres
conductor-es6-persistenceIndexing using Elasticsearch 6.X
conductor-restSpring MVC resources for the core services
conductor-uinode.js based UI for Conductor
conductor-contribsOptional contrib package that holds extended workflow tasks and support for SQS, AMQP, etc
conductor-clientJava client for Conductor that includes helpers for running worker tasks
conductor-client-springClient starter kit for Spring
conductor-serverSpring Boot Web Application
conductor-azureblob-storageExternal payload storage implementation using AzureBlob
conductor-redis-lockWorkflow execution lock implementation using Redis
conductor-zookeeper-lockWorkflow execution lock implementation using Zookeeper
conductor-grpcProtobuf models used by the server and client
conductor-grpc-clientgRPC server Application
conductor-grpc-servergRPC client to interact with the gRPC server
conductor-test-harnessIntegration and regression tests
核心亮点
  • 无状态
  • 多数据中心
  • 分布式锁
  • 基于中间件的任务队列
  • 可插拔组件
  • 模块化加载
Worker实现
* 实现Worker Interface
public class SampleWorker implements Worker {

    private String taskDefName;

    public SampleWorker(String taskDefName) {
        this.taskDefName = taskDefName;
    }

    @Override
    public String getTaskDefName() {
        return taskDefName;
    }

    @Override
    public TaskResult execute(Task task) {
        TaskResult result = new TaskResult(task);
        result.setStatus(Status.COMPLETED);

        //Register the output of the task
        result.getOutputData().put("outputKey1", "value");
        result.getOutputData().put("oddEven", 1);
        result.getOutputData().put("mod", 4);

        return result;
    }
}
  • 轮询任务队列
public static void main(String[] args) {

        TaskClient taskClient = new TaskClient();
        taskClient.setRootURI("http://localhost:8080/api/");        //Point this to the server API

        int threadCount = 2;            //number of threads used to execute workers.  To avoid starvation, should be same or more than number of workers

        Worker worker1 = new SampleWorker("task_1");
        Worker worker2 = new SampleWorker("task_5");

        // Create TaskRunnerConfigurer
        TaskRunnerConfigurer configurer = new TaskRunnerConfigurer.Builder(taskClient, Arrays.asList(worker1, worker2))
            .withThreadCount(threadCount)
            .build();

        // Start the polling and execution of tasks
        configurer.init();
    }
Demo展示
kitchensink.png