业务单系统架构设计心得(二):流程编排

阅读说明:

  1. 如果有排版格式问题,请移步 www.yuque.com/mrhuang-ire... 《业务单系统架构设计心得(二):流程编排》,选择宽屏模式效果更佳。
  2. 本文为原创文章,转发请注明出处。如果觉得文章不错,请点赞、收藏、关注一下,您的认可是我写作的动力。

上一节《业务单系统架构设计心得(一)》中,讲到了业务系统分层。本文对流程层继续深究,讨论流程层沉淀出通用组件。

业务复用编排

会用到编排的,大多是有业务复用,流程层根据业务需求串联起不同的子服务,上一节已经比较详细介绍了过程,这里不再详细展开叙述。这里补充一下串联方式,有以下三种方式:

  • 硬编码:在系统中代码写死要串联的服务;
  • 配置式:通过配置的方式进行串联,这种好处是比较灵活;
  • 注解式:java中的注解能够做到标记的作用,通过自定义注解实现串联;

异步查询编排

在实际业务中,一个app页面可能会加载很多信息,以美团外卖商家端为例: 因此,服务端必须一次性返回订单的所有信息,包含订单主信息、商品、结算、配送、用户信息、骑手信息、餐损、退款、客服赔付等。企业通常采用微服务架构,这些不同的模块通常部署在不同的机器上,因而需要发起多次rpc请求数据。如果继续采用同步加载的方式,这将带来一个新的问题,对外请求太多拖垮接口响应时间,商家的用户体验太差。

遇到性能的第一问题是使用线程池,使用多个线程并行处理任务,能够明显改善接口的性能。但是,这不是完美的,使用线程池虽然可以解决异步的问题,但是没有解决组合的问题。我们来对实际业务流程抽象,将流程中每个小步骤看做节点,那么业务可以简化成如下流程。

业务依赖简化版

该简化流程涵盖了实际业务中的可能出现的各种组合情况:

  • 零依赖:节点向下没有依赖,图中有节点CF1、 CF2、CF3。
  • 一元依赖:节点向下有一个依赖项,图中有节点CF4、CF6。
  • 两元或者多元依赖:节点向下有两个或者多个依赖项,图中两元依赖节点有CF5,三元依赖节点有CF7。
  • 两元或者多元被依赖:节点被向上两个或者多个节点依赖,图中有CF3。

显然,对于前后依赖的问题,java线程池没有很好的解决,而这是业务流程中经常会出现的场景。java8之后,新加了CompletableFuture,CompletableFuture就很好的解决了执行依赖的问题。

异步编排执行框架

本节将讲述基于CompletableFuture搭建异步编排执行框架,示例仍以上图业务依赖简化版为例。

1.构建依赖关系

构建依赖关系前,需要将流程进行拆分多个节点,确定节点两两之间的依赖关系。

2.生成依赖树

根据上一步两个节点之间的依赖关系,构建一颗完整的依赖树,这里需要记录下父节点与其依赖的所有子节点的关系。最下层没有依赖的节点是叶子节点,图中CF1, CF2, CF3就是叶子节点。

3.节点执行

节点执行分为两步。第一步,找到叶子节点,叶子节点因为没有依赖,可以直接执行。

4.节点唤醒

在第三步中,被依赖节点执行完时,需要唤醒上层节点,上层节点检查依赖的所有子节点是否都已完成,如果都完成,则执行本节点,直至最上层的节点也完成,整个流程结束。这其中有个关键的一点是,子节点与父节点之间如何唤醒,到这里能够想到通过注册回调事件来实现了。这里使用CompletableFuture来实现,关键代码如下:

java 复制代码
//节点1
CompletableFuture<String> childFuture1 = new CompletableFuture<>();
//节点2
CompletableFuture<String> childFuture2 = new CompletableFuture<>();
//节点3
CompletableFuture<String> childFuture3 = new CompletableFuture<>();

////节点4, 依赖节点1,2,3, 注册回调事件
CompletableFuture<String> childFuture4 = new CompletableFuture<>();


CompletableFuture.allOf(childFuture1, childFuture2, childFuture3)
                // 当node节点所有的父节点结果都构建完成时唤醒node节点
                .whenComplete((Void aVoid, Throwable throwable) -> {
                    {} //节点4的逻辑
                    childFuture4.complete("");
                });


{}//这里执行节点1的逻辑
childFuture1.complete("");


{}  //这里执行节点1的逻辑
childFuture2.complete("");


{} //这里执行节点1的逻辑
childFuture3.complete("");

使用异步编排的几个注意事项

1.CompletableFuture使用自定义线程池。手动传递线程池参数的好处是可以更方便的调节参数,并且可以给不同的业务分配不同的线程池,以求资源隔离,减少不同业务之间的干扰。 2.使用自定义线程池需要传递调用线程的上下文内容。子线程启动时,继承调用线程的上下文内容,比如调用线程的traceId等等,以及业务上塞入的线程ThreadLocal变量。子线程退出时,需要清理掉继承的信息。 3.线程池数量不能过多。线程过多时,同时处理太多请求,实际业务上通过rpc组件请求外部服务,请求将达到io线程上。如果是存在io耗时的请求,将导致io线程一直被占用,影响整个服务的相应,并没有达到异步提高性能的效果。

参考文献:

  1. docs.oracle.com/javase/8/do...
  2. tech.meituan.com/2022/05/12/...
  3. mp.weixin.qq.com/s/TdWgyDTTX...
相关推荐
Clank的游戏栈9 小时前
Unity帧同步与状态同步混合架构开发指南
java·unity·架构
nbsaas-boot12 小时前
从分层到微服务:构建高可扩展的 ERP 系统架构之道
微服务·架构·系统架构
魔法小匠12 小时前
微服务通信:用gRPC + Protobuf 构建高效API
微服务·云原生·架构·golang·grpc
凉、介13 小时前
ARM 架构下 cache 一致性问题整理
linux·汇编·arm开发·学习·缓存·架构
一水鉴天13 小时前
为AI聊天工具添加一个知识系统 之135 详细设计之76 通用编程语言 之6
开发语言·人工智能·架构
m0_7482574614 小时前
Linux系统编程:深入理解计算机软硬件体系和架构
linux·运维·架构
web1508541593514 小时前
GPU 硬件原理架构(一)
架构
小天努力学java14 小时前
【软考-架构】9.2、摘要-签名-PKI-访问控制-DOS-欺骗技术
架构
一水鉴天14 小时前
为AI聊天工具添加一个知识系统 之136 详细设计之77 通用编程语言 之7
开发语言·人工智能·架构
美狐美颜sdk15 小时前
美颜SDK架构揭秘:人脸美型API的底层实现与优化策略
图像处理·人工智能·深度学习·架构·视频美颜sdk·美颜api