作者:人月神话,新浪博客同名
今天谈下传统ESB服务总线里面的可视化服务设计,服务组合编排和微服务里面的服务编排。对于服务组合编排,实际上我们看到有几个不同的场景。
- 单服务可视化设计-仅仅针对一个服务实现
- 服务组合编排-实现多个服务的组合形成一个新的服务
- 业务流程编排-通过服务组合编排实现要给完整的业务流程
对于业务流程编排可以看到更多的是通过类似BPEL业务流程设计器来完成,因此今天主要介绍下传统ESB服务总线里面的单个服务设计,多服务组合设计编排,同时再介绍下NetflixConductor微服务编排的开源实现。
单个服务的可视化设计
单个服务的可视化设计可以理解为实现单个服务的可视化服务设计,其中包括了服务适配,路由,数据映射,协议转换等常见的编排节点和组件。
对于单服务设计可能用到的组件,我们基于服务集成场景,主要可以分为服务发布类组件,服务适配类组件,数据映射类组件。具体的组件包括了:
服务发布类组件(只需要支持SOAP WS服务和Rest WS服务即可)
- SOAP Proxy WS组件:发布代理服务
- SOAP Business WS组件:发布业务服务,衔接原始的业务服务地址
- Rest Proxy WS组件:发布基于Rest风格的代理服务
- Rest Business WS组件:发布业务服务,衔接原始的Rest业务服务地址
- SOAP WS Request组件和 SOAP WS Response组件
- Rest WS Request组件和 Rest WS Response组件
适配器组件
- DBSqlQuery组件:实现对数据库的Sql数据查询能力。
- DBSqlInsertOrUpdate组件:实现对数据库的Insert或Update操作适配能力。
- DBStoreProc组件:实现对数据库存储过程的适配能力。
- FTPInput或FileInput组件:实现对源端的数据获取能力
- FTPOutput或FileOutput组件:实现对FTP服务器目标端的适配能力
- JMSInput组件:实现对JMS写入能力
- JMSOutput组件:实现对JMS的消费和订阅能力
数据映射类组件
- XMLMApping组件:实现两个XML结构之间的数据映射能力
- tMapping组件:实现ETL时候两个数据集之间的数据映射能力
基于以上组件我们可以来看,常见的服务集成场景用上面的设计器组件基本就能够满足,同时实现最简单额设计组件之间的组合和连接。而设计器本身是一个设计态的东西,因此只需要将设计完成的内容,即设计元数据存储为一个独立的XML文件即可。
后续基于设计文件要做的就是进行实际的服务封装和部署,而这个时候才需要将设计器的内容进行解析,进行动态的服务封装和部署工作。以实现服务设计态和服务运行态的自动衔接能力。
多个服务组合编排
服务组合编排是服务组合,服务组装等,希望通过服务编排能够完成这些事情,而不是简单的完成单一服务的设计和开发。即将多个原子服务组合或组装在一起,最终形成一个新的服务并提供的能力。我们举例来说明下。
比如存在A,B,C三个原子服务,我们通过服务编排形成一个新的D服务。
三个原子服务全部是查询服务,希望组装一个新服务,一次返回A,B,C三个服务查询结果
这个即我们说的服务组合能力,比如我们可以对合同基本信息查询,合同条款信息查询,合同执行信息查询三个基本原子服务进行组合,最终返回一个服务综合信息查询的服务,一次返回三个查询结果。
在这种场景下我们需要考虑查询结果是并行返回还是按层次返回即可。
二个查询类的原子服务,最终需要返回两个数据集关联查询的结果集
这个在微服务架构做了底层数据库拆分后经常会遇到,比如对于物料基本信息查询,和采购订单明细查询是在两个独立的数据库独立服务提供。而我们希望返回的查询结果集是物料编码,名称,型号,单位,价格,采购数量的复合结果集。
这种场景下往往一般都是在前端功能开发的时候进行组装,而实际上可以考虑是否可以在服务编排层解决这个问题,该问题写代码来解决容易,但是要做为可视化服务编排组态方式来做实际上有一定的难度。
对单个已有服务进行裁剪和丰富并形成一个新服务输出
这个暂时也将其纳入到服务编排的范畴,即仍然是输入服务,但是输出是提供了一个新服务。
即对单个已有的服务进行服务裁剪和丰富,比如对于输出结果过滤掉一些数据项,对于输入固定输入一些数据项等。这些简单的服务裁剪,丰富,或简单的数据转换可以在服务编排的时候完成,并提供一个新服务。
对多个原子服务进行流程式的前后串接并形成服务提供
这个是我们经常看到的一种服务编排场景,即A,B,C三个服务直接进行编排,即A服务的输出直接变为B服务的输入,B服务的输出又变为C服务的输出。如果仅仅是上面假设的这样,那么这种流程式的服务编排仍然很简单,也很容易去实现。
但是实际上的难点在于A服务的输出本身也需要作为C服务的输出,同时A,B服务的输出也可能是整体输出的一部分,这本身就加大了服务编排可视化设计的难度。
单一业务服务为主体服务,但是编排多个业务规则逻辑处理类服务
这也是经常会遇到的场景,比如我们在进行合同信息导入的时候,首先要调用合同有效性校验服务,同时还有调用预算信息检查和扣减服务进行相关的完整性和业务规则校验。在这些校验完成后再调用实际的合同信息导入服务,如果校验失败则直接返回失败结果。
这类服务编排往往也正是我们实际在进行前端功能开发时候服务进行组装的逻辑。
多个导入服务组装为一个导入服务合并导入并形成一个新服务
这个场景实际上和场景1是对应的,既然多个服务可以组合后形成组合结果返回,那么自然可以将多个导入服务合并为一个导入服务,一次性的完成数据导入。
比如有项目信息导入和项目WBS信息导入两个原子服务,那么我们就可以提供一个新的项目信息导入服务,一次完成项目基本信息和项目WBS信息的导入。
在这些场景里面可以看到,实际上服务编排就是服务串联,服务并联下的输入和输出合并,服务内容丰富和裁剪等常见场景。在一个理想的场景下,我们最希望实现的就是一个业务功能点的实现完全能够通过服务编排可视化设计方式来完成。
下面我们来分析和讨论下服务编排的可视化设计。
a. 定义一个新服务,需要提前考虑新服务输入和输出结构设计
要进行服务编排,实际上编排完成后是新产生一个新服务,那么新服务一定有输入和输出结构,那么就需要对新服务的输入和输出结构进行设计。一种方法是在服务编排的时候再对输入和输出结构进行定义,一种方法是提前对新服务的服务契约进行定义,服务契约定义好后就形成了标准的服务输入和输出结构。
对于定义的服务编排产生的新服务,拖拽到设计器后应该产生输入和输出两个独立节点,这个在我们做单服务的ESB服务设计器的时候思路是一致的。
b. 服务之间的连接,本质是服务输入和输出之间的连接和映射
要明白服务之间的连接本质是输入和输出之间的连接和映射。以三个原子服务全部是查询服务,我们希望组装一个新服务,一次返回A,B,C三个服务查询结果,这个场景来举例说明。
我们需要拖拽A,B,C三个原子服务节点到设计器里面,然后将新服务的输入连线到A,B,C三个原子服务节点。在连接完成后,我们需要将新服务的输入和ABC三个服务的输入之间进行数据映射。
其次我们需要将ABC三个服务的输出连接到新服务的输出。对于新服务的输出,我们同样需要完成数据项之间的映射。但是这里的复杂性体现在ABC三个服务输出的三个结果集之间究竟是并行关系,还是父子层次关系,在进行数据映射和合并返回的时候,我们需要提前进行三个结果集的层次关系定义。这里面场景包括
- 结果集并行结构返回多个
- 结果集返回层一个层次结构的结果集返回
- 对多个结果集类似Sql一样进行查询关联,返回一个结果集作为结果
以上三种则是我们场景的对服务查询结果进行处理,合并或关联的方式。
c. 对服务流程式的串联和顺序处理,重点需要解决跨多节点数据映射问题
这个前面已经讲过,即将A,B,C三个服务进行串联方式编排的时候,实际我们看到B结果的输入只能够是A节点的输出,但是服务C的输入却可以同时是A或B的输出。因此在编排完成后进行数据映射的时候,一定需要支持C节点的输入可以同时映射到A或B的输出数据项元素。
d. 对于规则计算节点的两种可能,简单规则节点和复杂规则节点
对于规则节点需要考虑两种可能性,一种是常见的简单规则节点,即进行简单的加减乘除运算得出一个规则,基于规则判断后能够走不同的分支对结果进行处理。另外一种可能即将输入送入到规则引擎进行处理,处理完成后返回一个结果,如果在不连接规则引擎的情况下,我们可以设计一个专门的WS服务节点,即该节点可以去调用外部服务并返回输出结果,并基于输出结果情况进行判断,基于判断走不同的分支。
注意这种规则WS服务节点仅仅是进行规则处理,而非整个服务编排的主体输入和输出。实际我们在进行服务编排设计的时候,最好不要将这类节点放在主体服务编排路径上面。
NetflixConductor微服务编排
对于服务编排的可视化设计,其中最核心的还是服务编排本身任务或活动节点对应的是原子服务,连线对应的是服务输入输出之间的映射,整个编排完成是形成一个新的接口服务能力,这就是服务编排要做的事情。如果无法满足上面的核心,那谈不上服务编排。
今天谈下开源的微服务编排Netflix Conductor,先说下具体的场景,这个是Netflix内容平台工程团队运行由微服务上执行的任务的异步编排驱动的多个业务流程。其中一些是长期运行的流程,跨越几天。这些流程在准备好标题流式传输给全球的观众上发挥关键作用。
这些流程的几个实例是:
- 用于内容提取的Studio合作伙伴集成
- 基于IMF的内容提取我们的合作伙伴
- 在Netflix中设置新标题的过程
- 内容提取、编码和部署到CDN
传统上,这些流程中的一些已经以ad-hoc方式使用pub/sub的组合来编排,进行直接REST调用,并使用数据库来管理状态。然而,随着微服务数量的增长和进程的复杂性增加,在没有中央编排器的情况下,获得对这些分布式工作流的可见性变得困难。我们将Conductor构建为一个编排引擎,以满足以下要求,取出在应用程序中需要的样板,并提供一个反应流:
基于蓝图。基于JSON DSL的蓝图定义执行流程。
- 跟踪和管理工作流。
- 能够暂停、恢复和重新启动进程。
- 能够扩展到数百万个并发运行的进程流。
- 由从客户端抽象的排队服务支持。
- 能够通过HTTP或其他传输方式进行操作,如 gRPC。
基于上面介绍可以看到Netflix Conductor最重要的还是实现了基本的工作流定义,任务定义,任务的连接,整个工作流的任务调度和监控等基本能力。
官方参考文档:https://github.com/Netflix/conductor
实例参考文档:https://cloud.tencent.com/developer/article/1367734
对于具体的功能说明和介绍参考上面两篇文章即可,在此不再重复进行描述,只对看完了整个Netflix Conductor功能实现后做一下简单总结。
在Netflix Conductor中任务节点的定义虽然可以定义详细的输入和输出,但是任务节点并不是服务引用节点,任务节点具体需要开发实现类来进行实现。那么我们场景里面说的任务节点即服务节点,任务的输入或输出就是服务的输入或输出是无法实现的,如果要实现也需要进行大量的定制开发才能够支持。
微服务编排完成后可以形成一个新的Http Rest服务接口,这个是我们需要的。同时对于编排完成的workflow本身是可以实现灵活的任务监控和任务调度,满足基本的流程引擎该有的功能。
没有看到可以进行可视化服务编排设计的地方,但是对于编排完成的模型文件可以展现为可视化的流程图展示,这个也是很多编排软件常用的做法。由于没有可视化设计,当前的输入输出数据项映射也在手工编写流程模板文件的时候完成数据映射工作。但是可以实现前面多个节点的输出朝后续节点传递的需求。
工作流设计完成后,可以看到仍然需要写大量的代码和实现类才能够完成工作流的运行,因此可以看到该开源软件并不能实现完全的面向业务或开发人员的可配置零编码的效果。
基于以上初步分析可以看到,Netflix Conductor开源微服务编排框架并不满足我们前面描述的微服务编排场景,如果要实现服务和服务之间的编排,实际上对该开源软件的定制和改造工作量相当大。因此在我们实现微服务编排的时候并不建议选择该开源软件。其次,在整个微服务架构体系中,也不建议采用Netflix Conductor,至少在前期的改造过程中使用的场景很小,完全可以用其他方式来替代。