Q&A

Q:LiteFlow支持事务么?

A:不能说支持或者说不支持,因为LiteFlow和事务没有本质上的关系。LiteFlow只不过在本地帮你把代码进行组件化和可编排化,事务还是按照原先的方式去做。例如,你完全可以加@Transactional来开启spring事务:

  1. @Transactional
  2. public void testIsAccess() {
  3. LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1", 101);
  4. }

那么,这个链路中的所有组件,只要有一个组件发生异常,那么执行过的本地事务就会回滚,同理,你可以在任意地点加编程式事务。

同理,如果涉及到分布式事务,你也可以采用任意一种分布式事务的解决方案来做,这本质上已经脱离了LiteFlow的讨论范畴。

Q:是否可以做审批流或者角色轮转的流程?

A:其实在开篇LiteFLow是什么已经有提到过,LiteFlow不做基于角色流转的流程,只做逻辑流程。并且LiteFlow在以后,也不会做基于角色流转的流程,因为LiteFlow要保持轻量和易用性,是一个无状态的流程编排工具。如果你的业务是基于角色流转的,推荐使用Flowable。

Q:LiteFlow性能如何?

A:LiteFlow本身性能优秀,几乎没有什么额外的损耗,在压测过程中,基于复杂的价格逻辑引擎的业务系统,三十多个组件,在实测中可以跑到单机1500多的TPS。当然,这是基于良好的组件实现逻辑的前提下。如果你的组件里有一个bad sql,或者大量的IO操作,RPC调用操作,那么任何框架也无法提升你业务的TPS。这里只能说LiteFlow框架本身对系统几乎无额外损耗,如果你的系统使用了LiteFlow但是TPS/QPS很低的话,那么请从你的组件实现逻辑入手排查。

Q:是否支持逆向执行,来实现回滚等操作?

A:不支持,如果要实现本地回滚,请用事务来控制,如果涉及分布式事务的回滚,也有分布式事务的解决方案可以用。

但是之后的版本可能会出一个特性,在执行的过程中,如果遇到某个Exception去执行额外的链路,如果真的想逆向执行,可以把回滚组件放到这个里面。

Q:是否支持界面编排?

A:暂时不支持,但是在LiteFlow规划蓝图中,最终是要实现界面编排的。我本身不擅长前端,也请有这方面经验的同学能联系我。帮忙一起贡献开源。

Q:可以配置多个规则文件吗?

A:可以。用逗号或者分号隔开即可。

Q:如何做高可用,分布式的编排?

A:LiteFlow是轻量级的单服务编排,你可以把它理解为一个工具包。和高可用,分布式没有关系。

你一个业务系统里面有50个组件,liteflow可以编排,复杂一点的也可以。但是你多个业务系统,想要用一个链路,去编排不同服务里的组件。先去调用A服务的组件a,再去调用B服务的组件b,再去调用C服务的组件c,这种LiteFlow并不支持。

变相的实现,只有你独立出来一个服务X,然后服务X写3个组件(x1,x2,x3)分别用rpc去调用a,b,c,然后把x1,x2,x3编排成一个链路。

但是对LiteFlow来说,它运行的组件也只是X服务中的3个组件,至于组件里面是rpc调用还是其他网络IO操作,这和LiteFlow本身没有关系,因为已经涉及到业务实现层了。

Q:DataBus为什么采用queue和array来维护数据槽索引和数据槽对象的关系,为什么不直接使用数组?

A:在早期版本中,的确是用数组来维护SLOT的存储,为了解决并发问题,在offerSlot这个方法上采用synchronized来控制,这样会存在3个问题:

1.这种重量级锁性能很差

2.而且存在非公平的竞争关系,竞争会进一步增加系统的开销。

3.数组为了获取到可用的SLOT,会进行遍历,直到获取到可用的,如果前面都1000个都被占用了,就要遍历1001遍才能获取到可用的。

为了性能考虑,后来的版本采用了无锁化设计,采用CAS来解决,这样更加优雅且性能高。在开源框架中,能不用重量级锁就不用,无锁化且保证并发安全性是最优雅的做法。

Q:DataBus的数据槽索引队列容量存在获取为null的情况?动态扩容?

A:offerslot的代码如下:

  1. public static int offerSlot(Class<? extends Slot> slotClazz) {
  2. try {
  3. Slot slot = slotClazz.newInstance();
  4. Integer slotIndex = QUEUE.poll();
  5. if (ObjectUtil.isNotNull(slotIndex) && SLOTS.compareAndSet(slotIndex, null, slot)) {
  6. OCCUPY_COUNT.incrementAndGet();
  7. return slotIndex;
  8. }
  9. } catch (Exception e) {
  10. LOG.error("offer slot error", e);
  11. return -1;
  12. }
  13. return -1;
  14. }

理论上如果超过默认值1024的情况下,poll出来的会为null,而会进入异常处理,返回-1,-1在FlowExecutor里会报出NoAvailableSlotException的异常。

至少在压测和实际环境中,目前offerSlot的逻辑并未碰到过异常情况,如果有,请提供测试用例和代码,发现后会立即修复。

关于动态扩容,其实动态扩容在落地实践中,显得很鸡肋,因为LiteFlow提供了配置slotSize的大小,完全可以根据你项目的情况来作相应的配置,而且即便把这个数值配置的很大,也不会占用很多的元数据。只是生成index序列而已,而不会产生实际内容。没有必要为了动态扩容而去动态扩容。

Q:chain嵌套的时候slot对象是共享还是独自的,slotIndex存在冲突可能?

A:LiteFlow对SLOT的定义是:同一个请求链路中,SLOT为共享数据。

所以嵌套的时候SLOT对象是共享的,如果是独立的,那就无法适用同一个请求,一个上下文这个设计理念。

SLOT只有在执行FlowExecutor.execute才会进行分配,然后会放在ThreadLocal里面,在碰到嵌套链路时,也会实际执行嵌套链路里的组件,其实本质上和放在同一个链路里没有太多区别,因为都是同一个线程(或者主线程派发的子线程)中,所以能实现SLOT的共享机制

这里还有个例外,就是隐式流程(关注这个概念可以参照使用详细指南/隐式流程一章),隐式流程也是会执行FlowExecutor.execute这个方法,但是在处理时,隐式流程不会去分配SLOT,而是把上主流程中的slotIndex传递过去,这样也就实现了即使是隐式流程,也能SLOT共享的特性。

slotIndex不可能存在冲突,这和offerSlot的逻辑有关。前面已经解释过了。

Q:监控的插入点方式?

A:目前监控的逻辑插入点是在NodeComponent里面的execute里面写代码做统计的,这块目前的确比较耦合,并没有抽出来专门做。监控这块目前的确未仔细去做,只是一个非常简陋的监控,等有界面管理的时候,这块会重新设计模型去花精力去完善。

Q:ScriptComponent和ScriptCondComponent里面都有loadScript方法,这个方法是不是应该抽出来一个接口,看放在哪里合适,比如解析模块?

A:这2个类的loadScript方法的确有重复,理论上可以抽象出一个抽象类再加一层。但是由于已经把scriptFactory和scriptExecutor已经抽出来独立封装方法了,重复也只是一行代码的重复,在写的时候就偷懒不想再加一层了,这属于代码优雅性问题,也许以后可以优化掉。

Q:为啥没有ParserBus?

A:因为偷懒没抽象出来做。可以有,可以使代码更加优雅易读。

Q:可视化编排和监控的未来规划?

A:LiteFlow未来一定会做可视化编排,可视化编排目前没有具体的书面方案,仅存在于作者本人的脑海中。既然问到了这个问题,那就小小的概括下

可视化编排会作为LiteFlow的一个插件发布,为一个服务端。可视化编排有三个主要大功能区:流程编排,流程管理,执行监控。结合注册中心(nacos,zk,etcd)来给业务系统下发规则文件,可以进行热修改,热加载。当然LiteFLow的核心包依然可以独立使用,你依然可以像以前那样使用LiteFlow。LiteFlow编排管理器只是作为一个自定义配置源的方式存在,结合UI和注册中心进行下发。所以即使有了界面编排,LiteFLow依然不改变其轻量易用的特性。

Q:没有merge规范和说明?

A:有,以前在群里发过。但是并未整合进官方站点文档。关于这块,也是一定要做的。我会花时间把贡献规划和说明详细的整上去。

开源靠作者个人的力量很难走的很远,作者个人维护好几个开源项目,精力也有限,以前也有小伙伴进行过代码贡献,但是我需要的是长久稳定的贡献者。大家一起,这个项目才能走的更远更好,作为一直致力于中国开源的技术人,我也希望中国的开源更加繁荣。