流程编排LiteFlow-业务代码解耦
LiteFlow真的是相见恨晚啊,之前做过的很多系统,都会用各种if else,switch这些来解决不同业务方提出的问题,有时候还要“切一个分支”来搞这些额外的事情,把代码搞得一团糟,毫无可读性而言。如何打破僵局?LiteFlow为解耦逻辑而生,为编排而生,在使用LiteFlow之后,你会发现打造一个低耦合,灵活的系统会变得易如反掌!
另外, LiteFlow 和 Activiti 们并不是同一个东西,而是面向不同的使用场景和需求。LiteFlow 更加轻量灵活,适合需要简单流程管理和动态配置的场景;而 Activiti 则是一个全面的 BPM 引擎,适合需要复杂业务流程管理和任务管理的场景。根据具体业务需求,可以选择合适的工具来实现流程编排。
背景
之前做过一个数据分发系统,需要消费kafka的数据,下游有不同的业务,每个业务可能有共同的地方,也有不同的地方,在经过各类的处理之后,最后数据分发到下游里面去。为了简化代码方便理解,我们定义4个Handler(A、B、C、D),然后有3个不同的业务,需要经过不同的Handler,整个流程如下。
如果要在一个代码实现上诉功能,我们第一反应可能是责任链设计模式,每个业务一条链路,在Spring中,类似下面的代码:
public abstract class Handler {
abstract void handler(Request request);
}
@Component
@Slf4j
public class HandlerA extends Handler{
@Override
public void handler(Request request) {
log.info("处理器1");
}
}
@Component
@Slf4j
public class HandlerB extends Handler {
@Override
public void handler(Request request) {
log.info("处理器2");
}
}
@Component
@Slf4j
public class HandlerC extends Handler{
@Override
public void handler(Request request) {
log.info("处理器3");
}
}
@Component
@Slf4j
public class HandlerD extends Handler{
@Override
public 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开源,专注于逻辑驱动流程编排,通过组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑。它以其轻量级、快速、稳定且可编排的特性,在业务流程管理、规则引擎、工作流、订单处理、数据处理、微服务编排以及智能化流程管理等领域都有广泛的应用前景。
二、它可以解决什么问题
对大部分不断迭代的代码来说,历史遗留的代码加上需要面对各类各样的需求,代码会变得越来越难维护,甚至变成屎山。我们想着不断的去进行解耦,不断的去进行切割拆分,还要兼顾新需求,就怕蝴蝶效应导致大故障,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 {
@Override
public void process() throws Exception {
Customizer contextBean = this.getContextBean(Customizer.class);
}
}
@Slf4j
@LiteflowComponent("b")
public class HandlerB extends NodeComponent {
@Override
public void process() throws Exception {
Customizer contextBean = this.getContextBean(Customizer.class);
}
}
@Slf4j
@LiteflowComponent("c")
public class HandlerC extends NodeComponent {
@Override
public void process() throws Exception {
Customizer contextBean = this.getContextBean(Customizer.class);
}
}
@Slf4j
@LiteflowComponent("d")
public class HandlerD extends NodeComponent {
@Override
public 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<>();
@Resource
private FlowExecutor flowExecutor;
@PostConstruct
private 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,甚至后续改了一小块也不知道对别的地方有没有影响。
总结
后续,如果面对产品经理“来自大领导的一个想法,我不知道后续还会不会一直做下去,反正先做了再说”这类需求,就可以自己定义一个LiteFlow的组件,既不污染主流程的代码,后续下线了删掉即可,赏心悦目。
文档&参考
1.【腾讯文档】业务处理复杂 https://docs.qq.com/flowchart/DZVFURmhCb0JFUHFD
2.【腾讯文档】业务处理复杂2 https://docs.qq.com/flowchart/DZXVOaUV5VGRtc3ZD
3.一文搞懂设计模式—责任链模式
4.LiteFlow官网
流程编排LiteFlow-业务代码解耦的更多相关文章
- 唱吧DevOps的落地,微服务CI/CD的范本技术解读----最大的难点并不是实际业务代码的编写,而是服务的监控和调试以及容器的编排
1.业务架构:从单体式到微服务 K歌亭是唱吧的一条新业务线,旨在提供线下便捷的快餐式K歌方式,用户可以在一个电话亭大小的空间里完成K歌体验.K歌亭在客户端有VOD.微信和Web共三个交互入口,业务复杂 ...
- 从spring源码汲取营养:模仿spring事件发布机制,解耦业务代码
前言 最近在项目中做了一项优化,对业务代码进行解耦.我们部门做的是警用系统,通俗的说,可理解为110报警.一条警情,会先后经过接警员.处警调度员.一线警员,警情是需要记录每一步的日志,是要可追溯的,比 ...
- flask使用blinker信号机制解耦业务代码解决ImportError: cannot import name 'app',以异步发送邮件为例
百度了大半天,不知道怎么搞,直到学习了blinker才想到解决办法,因为之前写java都是文件分开的, 所以发送邮件业务代码也放到view里面,但是异步线程需要使用app,蛋疼的是其他模块不能从app ...
- Serverless架构详解:开发者如何专注于业务代码本身?
本文来自腾讯云技术沙龙,本次沙龙主题为Serverless架构开发与SCF部署实践 演讲嘉宾:黄文俊,曾负责企业级存储.企业级容器平台等产品的架构与开发,目前主要负责SCF腾讯无服务器云函数产品相关. ...
- 使用Spring AOP实现业务依赖解耦
Spring IOC用于解决对象依赖之间的解耦,而Spring AOP则用于解决业务依赖之间的解耦: 统一在一个地方定义[通用功能],通过声明的方式定义这些通用的功能以何种[方式][织入]到某些[特定 ...
- jsplumbWithVue实现流程编排
最近业务需求,通过拖拽,实现流程编排,包括编排元素中的各种属性,刚开始拿到这个需求,一头雾水啊,找度娘渡了好久,FQ翻了很久,各种博客CSDN搜索,最终技术选型还是定在了jsplumb,这要感谢@萌级 ...
- 流程编排、如此简单-通用流程编排组件JDEasyFlow介绍
作者:李玉亮 JDEasyFlow是企业金融研发部自研的通用流程编排技术组件,适用于服务编排.工作流.审批流等场景,该组件已开源(https://github.com/JDEasyFlow/jd-ea ...
- 朱晔的互联网架构实践心得S2E1:业务代码究竟难不难写?
注意,这是我的架构实践心得的第二季的系列文章,第一季有10篇你也可以回顾. 见https://www.cnblogs.com/lovecindywang/category/1296779.html 最 ...
- .netcore+vue+elementUI 前后端分离---支持前端、后台业务代码扩展的快速开发框架
框架采用.NetCore + Vue前后端分离,并且支持前端.后台代码业务动态扩展,框架内置了一套有着20多种属性配置的代码生成器,可灵活配置生成的代码,代码生成器界面配置完成即可生成单表(主表)的增 ...
- Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)
最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...
随机推荐
- AtCoder Beginner Contest 329 F
AtCoder Beginner Contest 329F F - Colored Ball (atcoder.jp)(启发式合并) 问题陈述 有 \(N\) 个编号为 \(1, 2, \ldots, ...
- 手把手教你如何上传代码到gitee服务器
之前编写了几个适合新手的小项目, 为方便大家学习和下载代码, 决定把代码上传到gitee服务器. 不得不说,git是一个非常好用的代码版本管理工具, 本文手把手教大家如何将自己编写的代码上传到Gite ...
- 一文讲透CRC校验码-附赠C语言实例
一口君最近工作用到CRC校验,顺便整理本篇文章和大家一起研究. 一.CRC概念 1. 什么是CRC? CRC(Cyclic Redundancy Checksum)是一种纠错技术,代表循环冗余校验和. ...
- Java Web中的request,response,重定位与转发的详解
request与response响应 Web服务器接收到客户端的http请求,其会对每一次的http请求分别创建应该代表请求的request对象,和一个代表响应的response对象. request ...
- Windows添加软件开机自启动
两种方式 1.添加快捷方式到开始菜单 打开我的电脑找到C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup 文件夹, 如果难找的话可 ...
- P10789 [NOI2024] 登山
思路: 我们可以对于每个 \(i\) 找到它能跳到的最远的点和最近的点,倍增求一下 \(k\) 级祖先即可,令 \([l_i,r_i]\) 新表示 \(i\) 能跳到其祖先中深度在 \([l_i,r_ ...
- layui的各模块的基本用途是什么?
Layui 是一个流行的前端UI框架,提供了多个模块,每个模块有不同的功能和用途.以下是 Layui 的一些主要模块以及它们的基本用途: 核心模块(layui): Layui 的核心模块包含了一些核心 ...
- 神奇的C语言输出12天圣诞节歌词代码
12天圣诞节程序怎样运行?1988 年,一个令人印象深刻且令人敬畏的 C 代码,代号为 xmas.c,在国际混淆 C 代码竞赛中获胜.该程序甚至比其输出的"压缩"类型还要小,代表了 ...
- 基于GitLab+Jenkin-CICD方案实践
前言 笔录于2022- 官网:https://about.gitlab.com/ 参考文档:https://docs.gitlab.com/ee/ci/ 清华源:清华大学开源软件镜像站 | Tsing ...
- 【论文解读】transformer小目标检测综述
一.简要介绍 Transformer在计算机视觉领域迅速普及,特别是在目标识别和检测领域.在检查最先进的目标检测方法的结果时,我们注意到,在几乎每个视频或图像数据集中,transforme ...