今天重新梳理一下自己关于服务组合和服务可视化编排的一些思考。从整个服务层次结构来看,微服务底层首先提供原子服务,向上可以提供更粗粒度的组合服务能力。为什么要进行服务组合和编排?简单的说,就是将通用的可重用业务能力进一步下沉。其中一些通用的业务能力是开发者在前端开发时自行组合编排的。那么实际的内容应该下沉到一个统一的领域服务能力提供层。在前后端开发分离的情况下,其实前端人员往往对业务不熟悉、不精通。如果是简单的UI界面,多接口服务交互,前端是没有问题的。但是,对于业务场景和业务规则相关的服务组合,前端其实很难在一个清晰的业务情况下进行编排。比如一个订单提交,前端是准备数据调用接口,但实际一个订单提交涉及到订单维护、库存扣减、预算查询、支付请求生成等多个API接口能力。而这些如何组合,以什么顺序调用,已经和业务规则逻辑相关,往往需要事务控制。像上面这样的事情不适合做前端,应该通过服务组合来完成。即使没有可视化的服务组合编排工具,那么这部分工作也应该由微服务架构中的一个领域服务层来提供。简单的输入-组合输出这是开发中经常遇到的场景。例如,在实现订单查看功能时,订单详情界面往往会涉及到订单信息、用户详情、所订酒店信息、房间详情、支付信息等多项信息展示功能。如果是通过前端开发来完成,那么往往前端开发需要调用多个后台API接口服务来完成数据的获取和填充。通过服务组合,所有信息都可以通过单个组合服务调用返回。整个服务组合过程可以简化为:这张图中其实有两个关键点。一种是服务的输出可以选择某些数据项信息作为下游服务的输入。二是任何服务的输出信息都可以作为最终服务的输出组合。那么如何实现呢?整体思路我们可以借鉴传统ESB中服务组合设计的思路,即先定义一个新的组合服务,确定API接口服务的契约格式。然后基于这个新服务进行服务组合和数据映射。整体实施的难度其实体现在两点。一是数据映射节点的设计。数据映射需要是一个独立的设计节点,上一个接口服务的输出和下一个接口服务的输入之间的数据格式的映射和转换都在这个节点上完成。比如前面的例子,订单查询接口查询到的json数据中,只获取了userid信息,可以触发用户查询接口的调用。一个订单可以进行多方面的预订,所以这里需要获取一个roomidList的json数据作为入口传递给房间信息获取接口。因此,在映射中,不是简单的数据项映射,还涉及到数据集的映射。二是数据组合格式的处理。需要理解的是,实际最终输出的是一个组合的数据集,要返回给多个查询,所以数据集本身会有结构和层次表达。因此,在最终返回的数据集的数据映射中,需要对这种组合数据格式进行处理,包括各个独立接口服务返回的信息映射到哪一层,以及主节点的ID依赖关系。串行处理中的事务对于API接口服务是无状态的。因此,在调用多个服务进行串行编排时,并不是简单的输入输出和数据映射的组合。更重要的是分布式事务处理。对于服务编排中的分布式事务处理,实际上有两种推荐的方法。一是事务补偿,二是异步最终一致性。对于事务补偿,在提供服务编排和接入时,需要基于服务幂等性提供反向操作服务。对于异步最终一致性,需要在服务组合中提供底层的消息中间件,实现异步和消息重试能力。举一个简单的例子来说明。提交订单时,我们需要调用订单保存服务,保存成功后调用库存扣减服务接口扣减库存。同时向用户发送邮件通知订单提交成功。以上是三种服务的常见串行编排操作。在这个过程中,我们采用了订单保全和库存扣减的补偿机制。首先进行库存扣减,然后进行订单保全。订单保全失败,库存扣款将退还。对于邮件发送,我们使用异步接口,即保证交易的最终一致性。因此,在设计服务编排时,上游服务应该为编排提供一个幂等的逆向服务,方便下游服务调用异常时上游服务的回滚操作。对于发送消息、事件等接口服务,推荐使用消息中间件实现异步最终一致性。这样的话,即使调用失败,上游服务也不会回滚,而是在服务编排实现中重试服务。如果多次重试仍然失败,发送异常日志信息进行人工修复。传统BPEL流程编排的简化在传统的SOA构建和实施项目中,如果遇到复杂的服务组合和服务编排,一般会采用类似的BPEL来完成。例如在OracleSOA建设项目中,利用OracleBPEL流程设计器实现服务编排和组合。BPEL是BusinessProcessExecutionLanguage的缩写,意思是业务流程执行语言。它是一种基于XML的编程语言,用于描述业务流程。所描述的业务流程的每个步骤都是由Web服务实现的。2002年,IBM、BEA和Microsoft联合开发并推出了BPEL作为描述和协调Web服务的语言。描述本身也由Web服务提供,可以用作Web服务。BPEL的实际功能相当强大,协议转换、适配、数据映射、数据裁剪和丰富、分支判断逻辑、外部第三方接口服务调用等能力一应俱全。因此,它通常被认为是一个比较重量级的服务编排工具。BPEL设计的结果是XML格式文件,有严格的方法和步骤说明,接口服务本身也需要有严格的接口契约描述文件,如WSDL、XSD等。因此,目前的微服务编排很少使用像BPEL这样的服务编排工具。BPEL的服务编排基本上是面向设计和开发人员的,但是这里需要找到一种可以让业务建模人员和系统分析人员使用的方法来方便地组装和编排服务。对于服务组装,基本上类似于流程建模和设计的方法。服务组装的最终结果是组合服务或流程服务。在服务组装的过程中,还是会大量参考流程可视化建模和设计的方法,只是考虑如何尽可能的简化。与传统的BPEL服务编排相比,微服务编排需要简化以下内容。只编排服务,不做服务适配、协议转换等。只做数据映射,不做复杂的业务规则逻辑处理。只做简单的数据裁剪或富集,不做复杂的逻辑分支判断。以上三点是在实现服务组合和服务编排时需要考虑的点。否则整个服务编排会越来越复杂。服务编排本身并不是万能的。为复杂的规则实现和服务组合编写代码仍然是最好的方式。编排后,可以对服务进行监控。对于服务设计者编排的服务,它本身就是一个新的API接口服务。服务编排设计和流程设计其实在很多地方都是相似的。也就是说,既要提供服务设计功能,又要提供服务运行监控功能。对于组合服务的操作,请求者每次调用API组合服务,都应该生成一个接口服务实例。进入接口服务实例后,可以详细监控当前接口服务的运行状态,具体是各个编排节点的输入输出信息、运行日志和异常信息等。如果要实现整个服务编排,可以看出这不仅仅是一个简单的服务设计者问题,而是一个完整的类似于BPEL的服务编排管理系统,既包括设计状态,也包括服务运行容器和状态监控。通过服务编排构建领域服务,是后端拆分出来的微服务模块的中心。如果需要集成多个微服务API接口服务,领域服务能力在哪里?传统的方式一般有两种,一种是直接在前端开发中完成,一种是增加一个单独的领域服务模块,实现跨微服务中心的领域服务API能力接口。如果在前端实现服务组合存在两个问题,一是前端开发往往不太关心具体的业务规则和逻辑,让前端组合往往导致关键业务实现出错逻辑;二是前端组合后的部分内容将难以复用。比如当BS端和APP端同时存在时,这部分内容往往需要同时实现两次。因此,服务编排的内容更适合后端开发。而传统单体应用被分割成多个独立的微服务中心,开发者往往只熟悉自己负责的微服务模块业务。因此,即使需要在后端完成,也需要熟悉整体业务和应用架构的人员来完成。在谈到低代码开发平台的时候,也提到最好通过统一的服务层来实现前端开发和后端能力提供的解耦,即前端表单设计绑定API接口服务能力,而不是与后台对象和数据库有直接关系。这样,对于更复杂的业务规则的实现,我们可以通过代码实现API接口服务,然后统一访问。在整个APP应用开发过程中,前后端分离后,后台能力和API提供只需要半自动化,通过调用API接口实现前端表单设计,并添加了一些前端JS脚本。简单的规则处理,完全可以达到理想的低代码开发效果。
