写出个灵活的系统竟然可以如此简单!小白也能写出高级的Java业务!
一
最近正好公司里有个需求,一个短信业务接了多个第三方供应商,某些业务需要查询第三方供应商剩余的短信包数量去选择剩余量最多的渠道去批量发送。有些业务是指定了某个短信供应商,有些场景需要根据业务的值去动态判断该用哪个供应商。场景非常复杂,还经常变化。
以前的代码实在惨不忍睹,选择剩余量最多的渠道是一个个去查的,然后获得结果去比较。至于指定的供应商和根据业务的值去判断选择供应商则是用大量的if else去嵌套各种判断。每次看到这坨代码真的觉得太粗糙了。关键是有些供应商还经常变,新接的供应商需要替代旧的供应商,加入这一大坨代码里。业务的判断条件还时不时变化一下。
出了几次问题之后,领导看不下去了,叫我想办法去优化。
我理了理逻辑,整个关系图应该是这样的,其中我把一个个去查的变成了并行去查,为了节约串行去查的IO耗时问题。
其中有些复杂查库逻辑,判断幂等性的步骤我就去掉了。只挑选了关键的步骤画上去。
叫我去重构这个慢慢写也能写出,但是关键的是,每个步骤和判断逻辑还时不时变化下。这就要求我的代码非常灵活。所以在设计时,一直很苦恼该如何去设计。
二
在小组开交流会的时候,有其他组的小伙伴和我安利了一个开源框架-LiteFlow。
经过研究这款开源框架,发现LiteFlow是一个国产规则引擎,能够编排任意复杂的流程,还支持热刷新。这基本上完全契合我的需求啊!
文档很详细,很NICE,大概看了半天就全部学完了。发现新版本的LiteFlow的规则是用EL表达式来写的。语法总共数下来也就10个左右,非常好理解。
比如这种图:
在LiteFlow用规则表示就是:
<chain name="chain1"> THEN( a, WHEN(b, THEN(c, d)), e ) </chain>
其中THEN是代表串行,WHEN代表并行执行,这种语法,一看就很好理解。
再来看这个图:
在LiteFlow里规则表示就是:
<chain name="chain1"> THEN( a, WHEN( b, SWITCH(c).to(d,e) ), f ) </chain>
其中SWITCH关键字就是排他网关的意思,c这个组件是一个java类,根据执行的结果去选择到底应该执行d还是e。
所以这样嵌套多层也应该是毫无问题的。
LiteFlow的文档里作者给出了很详细的例子,还有一些复杂例子,比如:
这种复杂的例子用LiteFlow的表达式可以写成:
<chain name="chain1"> THEN( A, WHEN( THEN(B, C), THEN(D, E, F), THEN( SWITCH(G).to( THEN(H, I, WHEN(J, K)).id("t1"), THEN(L, M).id("t2") ), N ) ), Z ) </chain>
它的表达式还可以进行定义子变量,上述表达式又可以写成:
<chain name="chain1"> item1 = THEN(B, C); item2 = THEN(D, E, F); item3_1 = THEN(H, I, WHEN(J, K)).id("t1"); item3_2 = THEN(L, M).id("t2"); item3 = THEN(SWITCH(G).to(item3_1, item3_2), N); THEN( A, WHEN(item1, item2, item3), Z ); </chain>
其实对照图,仔细看,会觉得这种表达式还是很清晰的。运用到我那个短信系统里是绰绰有余的。
三
我研究了下,花了10分钟时间,就写出了我那个流程的表达式规则:
<?xml version="1.0" encoding="UTF-8"?> <flow> <chain name="channelSenderChain"> selectBestChannel = THEN( WHEN( channel1Query, channel2Query, channel3Query, channel4Query, channel5Query, channel6Query ), channelSelector ).id("branch1"); selectBizChannel = THEN( biz1, SWITCH(if_2).to( channel3, channel4, SWITCH(if_3).to(channel5, channel6).id("s3") ).id("s2") ).id("branch2"); THEN( packageData, SWITCH(if_1).to( channel1, channel2, selectBestChannel, selectBizChannel ), batchSender ); </chain> </flow>
我用了文档中提到的子变量的方式去写,这种写法更加清晰。其实我总结了一个小窍门就是:**再复杂的图,都可以拆分成一个个局部的整体,先定局部的小变量,然后在主要的流程里去引入这些局部变量就可以了。**反正我写这个图的流程就差不多10分钟。
至于一个个小组件。我就跟着文档里做了一遍,把原来的大逻辑改拆成一个个的小逻辑。封装在不同的组件里,给上相应的Id就可以了。
最后通过LiteflowExecutor触发下就可以了。
LiteflowResponse response = flowExecutor.execute2Resp("channelSenderChain", null, BatchMessageResultContext.class); BatchMessageResultContext context = response.getFirstContextBean(); if (response.isSuccess()){ log.info("执行成功,最终选择的渠道是{}", context.getFinalResultChannel()); }else{ log.error("执行失败", response.getCause()); }
非常简单有木有!!!
而且特别优雅!!!
三
我改成上面这种形式了之后,每一个小逻辑块之间就完全解耦了。当中数据的连接完全是靠上下文
进行连接的,在研究了LiteFlow的理念之后,我发现这理念特别好。直接把原先的耦合性特别强的代码给拆分开来了。
现在业务有变动的话,我只需要改写其中一个组件就可以了。而且组件是可以拿来复用的。之间的顺序也是可以随意切换的。这一切,只需要改规则文件即可。代码是完全不用动的。
我仔细翻看了文档,这框架还支持完全无缝的热刷新,虽然我的代码没用到这特,但是看起来真的是太厉害了,改变规则的编排连重启应用都不需要!!!不过我打死都不会用这个特性的,领导叫我改业务,我还想多报点工时,这个如果上线了,我就没法多报工时了。。。🤣
LiteFlow还有很多高级特性,比如隐式流程啊,事件回调啊,声明式组件,组件切面啊,步骤信息,线程池的自定义,私有投递,还有简单监控。这款国产规则引擎快要玩出花了,强大!
重点要说下LiteFlow的脚本组件这个功能 ,这个功能是我写好代码才发现的。我发现,如果用脚本组件的话会更灵活。
虽然LiteFlow支持热刷新,但也仅限于规则文件改变。你Java代码改变,还得重启。
但是LiteFlow的脚本组件连这层都给你捅破了,你可以定义脚本,还支持groovy脚本,这下,连改变逻辑都不用重启应用了。。
介于我上面的私心,我同样也不会把这功能告诉领导😅。
四
我重构完这个项目之后,发现LiteFlow这个框架的可玩点非常多。
虽然官方是宣称是规则引擎,适用于用来解耦系统,组件编排。但是我发现用它来做一些简单的异步线程编排也是非常nice的。我自己本身对多线程不太精通,用这个来写,太方便了。
LiteFlow除了规则文件之外,还支持代码形式的链式组装规则,这个特性正好用来写多线程。
比如,我要写一个这样的多线程例子:
要让我用CompletableFuture来写,我还真不太会。但是你用LiteFlow就很容易,在LiteFlow你无需定义线程,框架自己会为你创建线程,你只需要把你线程里的代码变成一个个组件,然后用代码定义规则就可以了。
写法如下:
String el = "THEN(" + " main1," + " WHEN(" + " THEN(c1, c2, WHEN(c3, c4))," + " THEN(c5, c6)" + " )," + " main2" + " )"; LiteFlowChainELBuilder.createChain().setChainName("testChain").setEL(el).build(); LiteflowResponse response = flowExecutor.execute2Resp("channelSenderChain", 流程初始参数, 你的上下文.class);
就这样即完成了这个看上去有点复杂的线程编排了。这款框架简直是对不会写多线程小白的福音啊。爱了爱了。
五
顺便还要说下的是,官网的文章超详细。社区群也很活跃。
为了方便理解,我特地把我那个短信的例子进行了mock后推到了github仓库,大家可以自行pull下来玩耍。
顺便放下LiteFlow的Gitee的仓库地址,大家可以关注下这款国产规则引擎

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
go-zero微服务实战系列(十、分布式事务如何实现)
在分布式应用场景中,分布式事务问题是不可回避的,在目前流行的微服务场景下更是如此。比如在我们的商城系统中,下单操作涉及创建订单和库存扣减操作两个操作,而订单服务和商品服务是两个独立的微服务,因为每个微服务独占一个数据库实例,所以下单操作就涉及到分布式事务问题,即要把整个下单操作看成一个整体,要么都成功要么都不成功。本篇文章我们就一起来学习下分布式事务的相关知识。 基于消息实现最终一致性 我们去店里就餐的时候,付钱点餐后往往服务员会先给我们一张小票,然后拿着小票去出餐口等待出餐。为什么要把付钱和取餐两个动作分开呢?很重要的一个原因是使他们的接客能力更强,对应到服务来说就是使并发处理能力更强。只要我们拿着小票,最终我们是可以拿到我们点的餐的,依靠小票这个凭证(消息)实现最终一致性。 对应到我们的下单操作来说,当用户下单后,我们可以先生成订单,然后发一条扣减库存的消息到消息队列中,这时候订单就算完成,但实际还没有扣减库存,因为库存的扣减和下单操作是异步的,也就是这个时候产生了数据的不一致。当消费到了扣减库存的消息后进行库存扣减操作,这个时候数据实现了最终一致性。 基于消息实现最终一致性这种策...
- 下一篇
得物前端唤端业务场景和技术精讲
原创 苏文康-得物技术 前言 当你在刷朋友圈时突然看到一个潮鞋广告,正是你非常喜欢、一直想买的那款而且价格美丽,于是你兴奋地点击广告直接打开了购物App,并且直接进入刚刚看到的潮鞋详情页,你只需要直接点击购买就能得到这双你期待已久潮鞋,这流程如丝般顺滑! 你正在疯狂追的爱豆在微博发了一款联名潮玩内容,还是独家发售,贴文中就有网页链接,你点击后直接打开购物平台进入了与爱豆联名同款的潮玩详情页,迫不及待的下单拥有一款时尚的潮玩! 我今天要和大家分享的是得物唤端技术,关于唤端网上已经有很多优秀的文章了,这里不会复读机式的把URL Scheme协议、Universal Link协议等再一遍遍唠叨了,这样可能你也会觉得毫无新意。 唤端分类 1. 剪切板式还原 场景1: 每当大促时节,由于线上广告载体平台大量封禁链接,导致电商平台只能采用发送文本的方式邀请亲朋好友给自己助力或者分享好物。其他用户复制整条信息,打开得物App,客户端检测到剪切板中有特定信息的内容,就能打开特定的页面。我们把这种方式称为剪切板式还原。 场景2: 除了转发给好友通过复制内容还原,还有一个场景也同样可以用到剪切板,那就是...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker安装Oracle12C,快速搭建Oracle学习环境