liteFlow源码解析

liteFlow简介

liteFlow是一个轻量级微流程框架.liteFlow能够帮助你的项目实现业务组件化
liteFlow能最大程度上解耦,支持即时调整策略的一个中间件

流程架构图

image

项目源码解析

以官网liteflow-example为例

1.自动装配

解析liteflow的starter中配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.yomahub.liteflow.springboot.LiteflowAutoConfiguration,\
  com.yomahub.liteflow.springboot.LiteflowExecutorInit
  • LiteflowAutoConfiguration
@Configuration
@EnableConfigurationProperties(LiteflowProperty.class)
@ConditionalOnProperty(prefix = "liteflow", name = "rule-source")
public class LiteflowAutoConfiguration {

    @Bean
    public ComponentScaner componentScaner(){
        return new ComponentScaner();
    }

    @Bean
    public FlowExecutor flowExecutor(LiteflowProperty property){
        if(StringUtils.isNotBlank(property.getRuleSource())){
            List<String> ruleList = Lists.newArrayList(property.getRuleSource().split(","));
            FlowExecutor flowExecutor = new FlowExecutor();
            flowExecutor.setRulePath(ruleList);
            return flowExecutor;
        }else{
            return null;
        }
    }
}

这个类设置了liteflow的配置参数, 关键在于判断liteflow配置文件的condition以及配置了ruleSource

扩展一下ComponentScaner

public class ComponentScaner implements BeanPostProcessor, PriorityOrdered {

	private static final Logger LOG = LoggerFactory.getLogger(ComponentScaner.class);

	public static Map<String, NodeComponent> nodeComponentMap = new HashMap<String, NodeComponent>();

	static {
		LOGOPrinter.print();
	}

	@Override
	public int getOrder() {
		return Ordered.LOWEST_PRECEDENCE;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		Class clazz = bean.getClass();
		if(NodeComponent.class.isAssignableFrom(clazz)){
			LOG.info("component[{}] has been found",beanName);
			NodeComponent nodeComponent = (NodeComponent)bean;
			nodeComponent.setNodeId(beanName);
			nodeComponentMap.put(beanName, nodeComponent);
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

关键点在于实现了BeanPostProcessor, 在nodeComponentMap中放入了所有的NodeComponent实现类(业务实现组件)

  • LiteflowExecutorInit
public class LiteflowExecutorInit implements InitializingBean {

    @Resource
    private FlowExecutor flowExecutor;

    @Override
    public void afterPropertiesSet() throws Exception {
        flowExecutor.init();
    }
}

init()具体代码, liteflow支持三种格式配置文件(xml, Java配置类, zk), 本文以xml配置文件为例

public void init() {
		XmlFlowParser parser = null;
		for(String path : rulePath){
			try {
				if(isLocalConfig(path)) {
					parser = new LocalXmlFlowParser();
				}else if(isZKConfig(path)){
					if(StringUtils.isNotBlank(zkNode)) {
						parser = new ZookeeperXmlFlowParser(zkNode);
					}else {
						parser = new ZookeeperXmlFlowParser();
					}
				}else if(isClassConfig(path)) {
					Class c = Class.forName(path);
					parser = (XmlFlowParser)c.newInstance();
				}
				parser.parseMain(path);
			} catch (Exception e) {
				String errorMsg = MessageFormat.format("init flow executor cause error,cannot parse rule file{0}", path);
				LOG.error(errorMsg,e);
				throw new FlowExecutorNotInitException(errorMsg);
			}
		}
	}

这个类初始化了流程执行器的所需的数据到FlowBus, parserMain的解析方法具体不再展开

2.组件介绍

执行器

执行器FlowExecutor用来执行一个流程,用法为

public <T extends Slot> T execute(String chainId,Object param)
public <T extends Slot> T execute(String chainId,Object param,Class<? extends Slot> slotClazz)
public <T extends Slot> T execute(String chainId,Object param,Class<? extends Slot> slotClazz,Integer slotIndex,boolean isInnerChain)
  • chainId: 流程流id
  • param: 流程入参
  • slotClazz: Slot的子类(业务中建议自己实现, 默认为DefaultSlot)
  • isInnerChain: 是否节点内执行

数据槽

在执行器执行流程时会分配唯一的一个数据槽给这个请求。不同请求的数据槽是完全隔离的。
默认的数据槽主要是一个ConcurrentHashMap,里面存放着liteFlow的元数据.还有一个双向队列ArrayDeque, 里面存放着步骤数据.所以建议根据业务自行实现.

组件节点

继承NodeComponent
实现process()方法来实现业务
建议实现isAccess()来判断是否进入该组件
其他几个可以覆盖的方法有:
方法isContinueOnError:表示出错是否继续往下执行下一个组件
方法isEnd:表示是否立即结束整个流程
在组件节点里,随时可以通过方法getSlot获取当前的数据槽,从而可以获取任何数据。

路由节点

<flow>
    <chain name="chain1">
        <then value="node1, node2, node3" />
        <then value="node4(node5 | node6)" />
        <when value="node7, node8" />
        <then value="node9, chain2" />
    </chain>
    
    <chain name="chain2">
        <then value="node9" />
    </chain>
</flow>

1.then节点代表顺序执行, when节点代表异步(同时执行),
2.node4节点是条件组件继承NodeCondComponent实现processCond()方法, 返回需要执行的节点字符串("node5"或"node6")
3. 2.3.0后支持子流程如在chain1中调用chain2, 也可以在代码内隐式调用

3.执行器的运作

FlowExecutor的excute方法

public <T extends Slot> T execute(String chainId,Object param,Class<? extends Slot> slotClazz,Integer slotIndex,boolean isInnerChain) throws Exception{
		Slot slot = null;
		try{
		    // 判断FlowBus中chainMap是否为空
			if(FlowBus.needInit()) {
			    // 初始化方法, 在自动装配中提到了
				init();
			}
            // 根据chainId获取对应的chain
			Chain chain = FlowBus.getChain(chainId);
            
			if(chain == null){
				String errorMsg = MessageFormat.format("couldn't find chain with the id[{0}]", chainId);
				throw new ChainNotFoundException(errorMsg);
			}
            // 如果不是子流程且数据槽还未获取(该方法有可能被invoke隐式代用)
			if(!isInnerChain && slotIndex == null) {
			    // 从DataBus中遍历slots取一个值为null的下标, 赋值一个slot实例后返回
				slotIndex = DataBus.offerSlot(slotClazz);
				LOG.info("slot[{}] offered",slotIndex);
			}

			if(slotIndex == -1){
				throw new NoAvailableSlotException("there is no available slot");
			}

			slot = DataBus.getSlot(slotIndex);
			if(slot == null) {
				throw new NoAvailableSlotException("the slot is not exist");
			}
            // 往当前slot增加一个当前时间的唯一ID
			if(StringUtils.isBlank(slot.getRequestId())) {
				slot.generateRequestId();
				LOG.info("requestId[{}] has generated",slot.getRequestId());
			}
            // 如果不是子流程则加当前入参和chaiId设置到slot
			if(!isInnerChain) {
				slot.setRequestData(param);
				slot.setChainName(chainId);
			}else {
			    // 主流程已有入参, 当前入参加到子流程的key
				slot.setChainReqData(chainId, param);
			}

			// 详解见Chain的execute()方法
			chain.execute(slotIndex);

			return (T)slot;
		}catch(Exception e){
			String errorMsg = MessageFormat.format("[{0}]executor cause error", slot.getRequestId());
			LOG.error(errorMsg,e);
			throw e;
		}finally{
			if(!isInnerChain) {
				slot.printStep();
				DataBus.releaseSlot(slotIndex);
			}
		}
	}

Chain的execute()方法

	public void execute(Integer slotIndex) throws Exception{
		if(CollectionUtils.isEmpty(conditionList)){
			throw new FlowSystemException("no conditionList in this chain[" + chainName + "]");
		}

		Slot slot = DataBus.getSlot(slotIndex);
        // 遍历chain中的conditionList(xml中then或者when节点)
		for (Condition condition : conditionList){
		    // 如果condition为then则顺序执行value中的node节点
			if(condition instanceof ThenCondition){
				for(Executable executableItem : condition.getNodeList()){
					try{
					    // 详解见Executable的execute方法
						executableItem.execute(slotIndex);
					}catch (ChainEndException e){
						break;
					}
				}
			// 如果condition为when则创建多个线程执行value中的node节点
			}else if(condition instanceof WhenCondition){
			    // 使用计数器
		    	final CountDownLatch latch = new CountDownLatch(condition.getNodeList().size());
				for(Executable executableItem : condition.getNodeList()){
					// 当前版本有两个WhenConditionThread, 一个在FlowExecutor中内部类, 一个单独抽离出来.(猜测内部类应该是抽离后忘记删了)
					new WhenConditionThread(executableItem,slotIndex,slot.getRequestId(),latch).start();
				}
				// 等待计数器完成或者超过15秒
				latch.await(15, TimeUnit.SECONDS);
			}
		}
	}

Executable的execute方法

	public void execute(Integer slotIndex) throws Exception {
		if(instance == null){
			throw new FlowSystemException("there is no instance for node id " + id);
		}
		instance.setSlotIndex(slotIndex);
		Slot slot = DataBus.getSlot(slotIndex);

		try{
		    // 可以重写isAccess()决定是否执行
			if(instance.isAccess()){
                // 详解见NodeComponent的excute方法
				instance.execute();
                // NodeComponent中有一个InheritableThreadLocal<Boolean> isEndTL, 来判断是否停止整个流程.
                // InheritableThreadLocal可继承,所以可以影响所有继承了NodeComponent的组件. 但是ThreadLocal是每个线程独有的, 所以对多线程的when并行流无效
				if(instance.isEnd()){
					LOG.info("[{}]:component[{}] lead the chain to end",slot.getRequestId(),instance.getClass().getSimpleName());
					// 抛出异常停止当前流程
					throw new ChainEndException("component lead the chain to end");
				}
			}else{
				LOG.info("[{}]:[X]skip component[{}] execution",slot.getRequestId(),instance.getClass().getSimpleName());
			}
		}catch (Exception e){
		    // 重写isContinueOnError()方法决定发生错误后是否继续执行(会使isEndTL无效)
			if(instance.isContinueOnError()){
				String errorMsg = MessageFormat.format("[{0}]:component[{1}] cause error,but flow is still go on", slot.getRequestId(),id);
				LOG.error(errorMsg,e);
			}else{
				String errorMsg = MessageFormat.format("[{0}]:component[{1}] cause error",slot.getRequestId(),id);
				LOG.error(errorMsg,e);
				throw e;
			}
		}finally {
		    // 移除NodeComponent中的InheritableThreadLocal<Integer> slotIndexTL(用于slotIndex的传递)
			instance.removeSlotIndex();
			// 移除isEndTL
			instance.removeIsEnd();
		}
	}

NodeComponent的excute方法

public void execute() throws Exception{
		Slot slot = this.getSlot();
		LOG.info("[{}]:[O]start component[{}] execution",slot.getRequestId(),this.getClass().getSimpleName());
		slot.addStep(new CmpStep(nodeId, CmpStepType.SINGLE));
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
        // 执行重写的业务方法(如果是NodeCondComponent组件会将要运行的nodeId绑定到solt中)
		process();

		stopWatch.stop();
		long timeSpent = stopWatch.getTime();

//		slot.addStep(new CmpStep(nodeId, CmpStepType.END));

		//性能统计
		CompStatistics statistics = new CompStatistics();
		statistics.setComponentClazzName(this.getClass().getSimpleName());
		statistics.setTimeSpent(timeSpent);
		MonitorBus.load().addStatistics(statistics);

        // condition组件
		if(this instanceof NodeCondComponent){
		    // 获取到绑定的nodeId, 并执行
			String condNodeId = slot.getCondResult(this.getClass().getName());
			if(StringUtils.isNotBlank(condNodeId)){
				Node thisNode = FlowBus.getNode(nodeId);
				Executable condExecutor = thisNode.getCondNode(condNodeId);
				if(condExecutor != null){
				    // 执行对应的NodeCompenent
					condExecutor.execute(slotIndexTL.get());
				}
			}
		}

		LOG.debug("[{}]:componnet[{}] finished in {} milliseconds",slot.getRequestId(),this.getClass().getSimpleName(),timeSpent);
	}
posted @ 2020-11-05 22:35  feixiong1688  阅读(3444)  评论(0编辑  收藏  举报