流程编排LiteFlow-业务代码解耦

news/2025/3/13 13:08:38/文章来源:https://www.cnblogs.com/w1570631036/p/18534261

LiteFlow真的是相见恨晚啊,之前做过的很多系统,都会用各种if else,switch这些来解决不同业务方提出的问题,有时候还要“切一个分支”来搞这些额外的事情,把代码搞得一团糟,毫无可读性而言。如何打破僵局?LiteFlow为解耦逻辑而生,为编排而生,在使用LiteFlow之后,你会发现打造一个低耦合,灵活的系统会变得易如反掌!

另外, LiteFlow 和 Activiti 们并不是同一个东西,而是面向不同的使用场景和需求。LiteFlow 更加轻量灵活,适合需要简单流程管理和动态配置的场景;而 Activiti 则是一个全面的 BPM 引擎,适合需要复杂业务流程管理和任务管理的场景。根据具体业务需求,可以选择合适的工具来实现流程编排。

背景

之前做过一个数据分发系统,需要消费kafka的数据,下游有不同的业务,每个业务可能有共同的地方,也有不同的地方,在经过各类的处理之后,最后数据分发到下游里面去。为了简化代码方便理解,我们定义4个Handler(A、B、C、D),然后有3个不同的业务,需要经过不同的Handler,整个流程如下。

image-20241108000137572

如果要在一个代码实现上诉功能,我们第一反应可能是责任链设计模式,每个业务一条链路,在Spring中,类似下面的代码:

public abstract class Handler {abstract void handler(Request request);
}@Component
@Slf4j
public class HandlerA extends Handler{@Overridepublic void handler(Request request) {log.info("处理器1");}
}@Component
@Slf4j
public class HandlerB extends Handler {@Overridepublic void handler(Request request) {log.info("处理器2");}
}@Component
@Slf4j
public class HandlerC extends Handler{@Overridepublic void handler(Request request) {log.info("处理器3");}
}
@Component
@Slf4j
public class HandlerD extends Handler{@Overridepublic void handler(Request request) {log.info("处理器4");}
}//然后我们定义一个枚举类,用来配置不同业务需要经历过的处理器。
public enum HandleBuz {Business_1(HandlerA,HandlerB),Business_2(HandlerB,HandlerC),Business_3(HandlerA,HandlerD);public final Class<? extends Handler>[] processors;public HandleBuz(Class<? extends Handler>[] processors){this.processors=processors;}    public void handle(){for (Handler handler : processors) {handler.handler(xxx);}}}

通过配置责任链,可以灵活地组合处理对象,实现不同的处理流程,并且可以在运行时动态地改变处理的顺序,由于责任链模式遵循开闭原则,新的处理者可以随时被加入到责任链中,不需要修改已有代码,提供了良好的扩展性。但实际上面对各种需求的时候,没法做到完全的解耦,比如对于HandlerA,如果业务1和业务2都有定制化的需求(来自产品提的临时或长期需求),此时是应该再HandlerA中用if else解决,还是再额外开个HandlerA_1和HandlerA_2。这类特性需求会非常多,最终把代码可读性变得越来越低。

一、为什么需要流程编排

LiteFlow由Baidu开源,专注于逻辑驱动流程编排,通过组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑。它以其轻量级、快速、稳定且可编排的特性,在业务流程管理、规则引擎、工作流、订单处理、数据处理、微服务编排以及智能化流程管理等领域都有广泛的应用前景。

img

二、它可以解决什么问题

对大部分不断迭代的代码来说,历史遗留的代码加上需要面对各类各样的需求,代码会变得越来越难维护,甚至变成屎山。我们想着不断的去进行解耦,不断的去进行切割拆分,还要兼顾新需求,就怕蝴蝶效应导致大故障,liteflow能帮我们在解耦上更加清晰一点。
(1)复杂业务流程编排和管理
在一些应用场景中,业务逻辑往往非常复杂,涉及多个步骤的执行,并且这些步骤之间具有复杂的依赖关系。LiteFlow 可以帮助开发者通过配置和代码相结合的方式定义和管理这些流程。
(2)流程动态配置
LiteFlow 允许通过配置文件或者数据库动态修改流程,而无需修改代码。这意味着可以根据不同的业务需求快速调整并发布新的流程,而不需要重新部署应用。
(3)流程节点的复用和解耦
在使用 LiteFlow 时,每个业务步骤都可以定义为一个独立的节点(Node),这些节点可以独立开发、测试和维护,并且可以在多个流程中复用。通过这种方式,可以实现业务逻辑的复用和解耦,提高代码的可维护性。
(4)节点状态和错误处理
LiteFlow 提供了丰富的节点状态管理和错误处理机制,允许开发者在流程执行过程中捕获和处理异常,从而确保系统的稳定性和健壮性。
(5) 高扩展性和自定义能力
LiteFlow 具有高度的扩展性,开发者可以根据自身业务的特殊需求定制节点、组件和插件,从而满足复杂场景的要求。

以下是一些实际使用 LiteFlow 的示例场景:
(1)订单处理系统:在电商系统中,订单处理涉及多个步骤,如库存检查、支付处理、订单确认和发货等。LiteFlow 可以帮助将这些步骤分开独立实现,然后通过流程引擎编排执行。
(2)审批流程:在企业中,审批流程通常包括多个节点(如申请、审批、复核、归档等),并且这些节点之间可能有条件和依赖关系。LiteFlow 可以帮助动态配置和管理这些流程,提高审批效率。
(3)营销活动:在一些营销活动中,不同的活动环节和逻辑可能会因用户行为和外部条件而变化。LiteFlow 可以帮助实现灵活的活动规则配置和执行。

三、LiteFlow改造之后

首先定义并实现一些组件,确保SpringBoot会扫描到这些组件并注册进上下文。

@Slf4j
@LiteflowComponent("a")
public class HandlerA extends NodeComponent {@Overridepublic void process() throws Exception {Customizer contextBean = this.getContextBean(Customizer.class);}
}@Slf4j
@LiteflowComponent("b")
public class HandlerB extends NodeComponent {@Overridepublic void process() throws Exception {Customizer contextBean = this.getContextBean(Customizer.class);}
}@Slf4j
@LiteflowComponent("c")
public class HandlerC extends NodeComponent {@Overridepublic void process() throws Exception {Customizer contextBean = this.getContextBean(Customizer.class);}
}@Slf4j
@LiteflowComponent("d")
public class HandlerD extends NodeComponent {@Overridepublic void process() throws Exception {Customizer contextBean = this.getContextBean(Customizer.class);}
}

同时,你得在resources下的config/flow.el.xml中定义规则:

<?xml version="1.0" encoding="UTF-8"?>
<flow><chain name="chain1">THEN(a,b);</chain><chain name="chain2">THEN(b,c);</chain><chain name="chain3">THEN(a,d);</chain>
</flow>

最后,在消费kafka的时候,先定义一个ruleChainMap,用来判断根据唯一的id(业务id或者消息id)来判断走哪条chain、哪个组件等,甚至可以定义方法级别的组件。

    private Map<Integer, String> ruleChainMap = new HashMap<>();@Resourceprivate FlowExecutor flowExecutor;@PostConstructprivate void init() {ruleChainMap.put(1, "业务1");ruleChainMap.put(2, "业务2");ruleChainMap.put(3, "业务3");}@KafkaListener(topics = "xxxx")public void common(List<ConsumerRecord<String, String>> records) {for (ConsumerRecord<String, String> record : records) {...String chainName = ruleChainMap.get("唯一id(可以是record里的,也可以全局定义的id)");LiteflowResponse response = flowExecutor.execute2Resp(chainName, xxx, xxx, new TempContext());}}

由于篇幅的关系,这里不再讲解怎么传递上下文的关系,可以自己去官网研究一下。另外,上面的例子因为是简化之后的,如果你觉得不够形象,可以看看下面的实际业务。这个如果不使用liteflow,可能就得在主流程代码里增加各种if else,甚至后续改了一小块也不知道对别的地方有没有影响。

image-20241108000231371

总结

后续,如果面对产品经理“来自大领导的一个想法,我不知道后续还会不会一直做下去,反正先做了再说”这类需求,就可以自己定义一个LiteFlow的组件,既不污染主流程的代码,后续下线了删掉即可,赏心悦目。

文档&参考

1.【腾讯文档】业务处理复杂 https://docs.qq.com/flowchart/DZVFURmhCb0JFUHFD
2.【腾讯文档】业务处理复杂2 https://docs.qq.com/flowchart/DZXVOaUV5VGRtc3ZD
3.一文搞懂设计模式—责任链模式
4.LiteFlow官网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/828588.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中awk命令实现指定字段的第一个字母大写

001、[root@PC1 test]# ls a.txt [root@PC1 test]# cat a.txt ## 测试数据 abcd KHG DETT dre ssae rtE [root@PC1 test]# awk {$1 = toupper(substr($1, 1, 1)) substr($1,2); print $0} a.txt ## 此处借助awk的内置函数…

java学习11.7

继续学习spring的内容

高级语言程序设计第六次作业

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C/ 这个作业要求在哪里: https://edu.cnblogs.com/campus/fzu/2024C/homework/13303 学号:092300125 姓名:张天荣#include<stdio.h>int P(int num){int i;for(i=2;i<=num/2;i++){if(num%i==0)return…

【Maple2023软件下载与安装教程】

1、安装包Maple 2023: 链接:https://pan.quark.cn/s/8141b75ee5b5 提取码:d6AZ Maple 2022: 链接:https://pan.quark.cn/s/c726906349f2 提取码:geCP Maple 2021: 链接:https://pan.quark.cn/s/a5f710946c9a 提取码:fGKB Maple 2020: 链接:https://pan.quark.cn/s/83fd1…

SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能

SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能@目录项目整体描述页面图简介:功能概括说明具体功能举例代码介绍首页index.html操作成功跳转hello.html首页跳转index.html代码方法1:读取指定的Excel方法2:读取上传的Excel里面的内容方法3:java单文件…

2024-2025-1 20242407《网络》第二周学习总结

2024-2025-1 20242407《网络》第二周学习总结 教材学习内容总结教材学习中的问题和解决过程 问题一:对于维吉尼亚密码的加密方式不是很理解 解决方法:通过询问AI基于AI的学习

WPF StatusBar控件 这一块也能放一些东西

WPF StatusBar控件 这一块也能放一些东西StatusBar控件一般在窗口的底部。用于显示有关应用程序当前状态的各种信息,如光标位置、字数、任务进度等。<Window x:Class="WpfApp14.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentati…

Multi-Scale and Detail-Enhanced Segment Anything-1-LMSA-轻量级多尺度适配器

代码 插图 均来自官方开放资源 用自适应的全局平均池化获得不同尺寸的大小(设置的值就是每层经过池化之后的大小) 再用上采样 让不同尺寸的特征图来到原始大小上 然后将它们和原特征图在深度方面叠加` import torch.nn as nn import torch import torch.nn.functional as F …

线性dp

线性dp 线性dp是一种很常见的dp分析方式,关系之间具有线性的关系,状态之间相互递推,之后求出最终状态,今天完成了两道dp的题目第一个问题看似是四维打表实际上使用线性的方式分析可以很快的求出结果没必要维护三维的空间 第二个问题要记录数列的个数,我们首先要知道几个递推关系…

企业网站如何修改首页,如何在企业网站后台或代码编辑器中修改首页内容

修改企业网站的首页内容可以提升品牌形象和用户体验。以下是修改首页内容的步骤:登录网站后台:打开浏览器,输入网站的后台地址,例如 http://yourdomain.com/admin。 输入管理员账号和密码,点击“登录”。进入首页管理:登录后,点击顶部菜单栏中的“首页”或“页面”。 选…

uni-app组件知识记录

目录style标签的lang视图容器组件scroll-view的使用(滚动)组件swiper的使用(轮播)媒体组件组件image的使用(图片)路由与页面跳转navigatortabBar底部导航配置表单提交组件动态赋值子组件的属性字段propsVUE语法data 属性指令v-if/v-elsev-showv-for循环v-html标签v-bind动态绑定…

SpringBoot获取项目文件的绝对路径和相对路径

SpringBoot获取项目文件的绝对路径和相对路径@目录1.场景2.说明项目代码大致样式获取路径说明3.举例说明网上常见几种方法的路径获取结果 1.场景比如上传图片或者读取项目里的excel文件内容等,都需要准确获取文件路径2.说明 项目代码大致样式获取路径说明所谓获取的相对路径,…