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

阅读说明:

  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...
相关推荐
程序员ys1 小时前
微前端是什么?
微服务·架构·前端框架
Goboy1 小时前
从零开始,用JupyterLab和TensorFlow打造你的第一个猫狗识别模型
后端·程序员·架构
聚搜云—服务器分享1 小时前
阿里云国际站代理商:传统IOE架构向云原生迁移的关键挑战有哪些?
阿里云·云原生·架构
鲨鲨1081 小时前
隐匿视角:七款局域网屏幕监控软件对企业数字神经系统架构的重塑效应探究
架构
威视锐科技8 小时前
软件定义无线电36
网络·网络协议·算法·fpga开发·架构·信息与通信
JINX的诅咒8 小时前
CORDIC算法:三角函数的硬件加速革命——从数学原理到FPGA实现的超高效计算方案
算法·数学建模·fpga开发·架构·信号处理·硬件加速器
二进制coder15 小时前
DFX架构详解:构建面向全生命周期的卓越设计体系
架构
Mia@16 小时前
网络通信&微服务
微服务·云原生·架构
Cloud_.19 小时前
用Nginx实现负载均衡与高可用架构(整合Keepalived)
nginx·架构·负载均衡·keepalived
Allen_LVyingbo21 小时前
文章配图新纪元:OpenAI新推出的GPT-4o原生图像生成功能启示
人工智能·学习·架构·数据分析·健康医疗