mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-05-14 04:02:09 +08:00
feature #IDGGGC Java脚本启动时批量编译,加快启动速度
This commit is contained in:
@@ -49,6 +49,7 @@ import com.yomahub.liteflow.util.ElRegexUtil;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -66,6 +67,8 @@ public class FlowExecutor {
|
||||
|
||||
private LiteflowConfig liteflowConfig;
|
||||
|
||||
private AtomicBoolean startUpPhase = new AtomicBoolean(false);
|
||||
|
||||
public FlowExecutor() {
|
||||
// 设置FlowExecutor的Holder,虽然大部分地方都可以通过Spring上下文获取到,但放入Holder,还是为了某些地方能方便的取到
|
||||
FlowExecutorHolder.setHolder(this);
|
||||
@@ -91,6 +94,8 @@ public class FlowExecutor {
|
||||
* isStart表示是否是系统启动阶段,启动阶段要做额外的事情,而因为reload所调用的init就不用做
|
||||
*/
|
||||
public void init(boolean isStart) {
|
||||
startUpPhase.compareAndSet(false, true);
|
||||
|
||||
if (ObjectUtil.isNull(liteflowConfig)) {
|
||||
throw new ConfigErrorException("config error, please check liteflow config property");
|
||||
}
|
||||
@@ -242,6 +247,8 @@ public class FlowExecutor {
|
||||
if (liteflowConfig.getChainCacheEnabled()) {
|
||||
evaluateChainCacheCapacity();
|
||||
}
|
||||
|
||||
startUpPhase.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
// 此方法就是从原有的配置源主动拉取新的进行刷新
|
||||
@@ -709,4 +716,8 @@ public class FlowExecutor {
|
||||
+"it is recommended to be greater than 30% of the number of chains", capacity, chainNum);
|
||||
}
|
||||
}
|
||||
|
||||
public AtomicBoolean getStartUpPhase() {
|
||||
return startUpPhase;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.yomahub.liteflow.script;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.common.entity.ValidationResp;
|
||||
import com.yomahub.liteflow.enums.ScriptTypeEnum;
|
||||
import com.yomahub.liteflow.exception.LiteFlowException;
|
||||
import com.yomahub.liteflow.lifecycle.LifeCycleHolder;
|
||||
@@ -37,6 +38,12 @@ public abstract class ScriptExecutor {
|
||||
|
||||
public abstract void load(String nodeId, String script);
|
||||
|
||||
// 第二段load,通常指合并一起编译/执行
|
||||
// 如果实现了第二段,说明第一段并未真正编译,而是把需要编译的存起来,留到第二段的时候一起编译
|
||||
public void loadSecondPhase(){
|
||||
|
||||
}
|
||||
|
||||
// 卸载脚本(不包含 node)
|
||||
public abstract void unLoad(String nodeId);
|
||||
|
||||
@@ -101,6 +108,15 @@ public abstract class ScriptExecutor {
|
||||
*/
|
||||
public abstract Object compile(String script) throws Exception;
|
||||
|
||||
public ValidationResp validate(String script){
|
||||
try {
|
||||
compile(script);
|
||||
} catch (Exception e) {
|
||||
return ValidationResp.fail(e);
|
||||
}
|
||||
return ValidationResp.success();
|
||||
}
|
||||
|
||||
public boolean executeIsAccess(ScriptExecuteWrap wrap){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ public class ScriptExecutorFactory {
|
||||
}
|
||||
|
||||
public void cleanScriptCache() {
|
||||
this.scriptExecutorMap.forEach((key, value) -> value.cleanCache());
|
||||
this.scriptExecutorMap.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,12 +58,7 @@ public class ScriptValidator {
|
||||
}
|
||||
|
||||
ScriptExecutor scriptExecutor = (scriptType != null) ? scriptExecutors.get(scriptType) : scriptExecutors.values().iterator().next();
|
||||
try {
|
||||
scriptExecutor.compile(script);
|
||||
} catch (Exception e) {
|
||||
return ValidationResp.fail(e);
|
||||
}
|
||||
return ValidationResp.success();
|
||||
return scriptExecutor.validate(script);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</parent>
|
||||
|
||||
<!-- This module is deprecated and should not be used. -->
|
||||
<!-- It is replaced by liteflow-script-javax module and not update anymore. -->
|
||||
<!-- It is replaced by liteflow-script-javax-pro module and not update anymore. -->
|
||||
<artifactId>liteflow-script-java</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.yomahub.liteflow.script.javaxpro;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.common.entity.ValidationResp;
|
||||
import com.yomahub.liteflow.core.FlowExecutorHolder;
|
||||
import com.yomahub.liteflow.core.FlowInitHook;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.enums.ScriptTypeEnum;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
@@ -12,12 +17,17 @@ import com.yomahub.liteflow.script.exception.ScriptLoadException;
|
||||
import com.yomahub.liteflow.script.javaxpro.vo.JavaxProSettingMapKey;
|
||||
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
|
||||
import org.noear.liquor.eval.CodeSpec;
|
||||
import org.noear.liquor.eval.Execable;
|
||||
import org.noear.liquor.eval.Scripts;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Javax语言执行器,基于liquor
|
||||
@@ -26,6 +36,9 @@ import java.util.Map;
|
||||
* @since 2.13.0
|
||||
*/
|
||||
public class JavaxProExecutor extends ScriptExecutor {
|
||||
|
||||
private final Map<CodeSpec, String> codeSpecMap = new HashMap<>();
|
||||
|
||||
private final Map<String, NodeComponent> compiledScriptMap = new CopyOnWriteHashMap<>();
|
||||
|
||||
private boolean isCache;
|
||||
@@ -37,13 +50,27 @@ public class JavaxProExecutor extends ScriptExecutor {
|
||||
isCache = Boolean.parseBoolean(isCacheValue);
|
||||
//如果有生命周期则执行相应生命周期实现
|
||||
super.lifeCycle(null);
|
||||
|
||||
// 注册第二段批次编译
|
||||
FlowInitHook.addHook(() -> {
|
||||
loadSecondPhase();
|
||||
codeSpecMap.clear();
|
||||
return true;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(String nodeId, String script) {
|
||||
try{
|
||||
compiledScriptMap.put(nodeId, (NodeComponent) compile(script));
|
||||
boolean startUpPhase = FlowExecutorHolder.loadInstance().getStartUpPhase().get();
|
||||
if (startUpPhase){
|
||||
codeSpecMap.put((CodeSpec) compile(script), nodeId);
|
||||
}else{
|
||||
compiledScriptMap.put(nodeId, (NodeComponent)compile(script));
|
||||
}
|
||||
|
||||
|
||||
}catch (InvocationTargetException e){
|
||||
String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getTargetException().getMessage());
|
||||
throw new ScriptLoadException(errorMsg);
|
||||
@@ -53,6 +80,21 @@ public class JavaxProExecutor extends ScriptExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// 第二段编译,指的是合并编译,如果实现了这个方法,说明compile阶段没有真正编译
|
||||
@Override
|
||||
public void loadSecondPhase() {
|
||||
if (MapUtil.isEmpty(codeSpecMap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<CodeSpec, Execable> execableMap = Scripts.compile(new ArrayList<>(codeSpecMap.keySet()));
|
||||
|
||||
execableMap.forEach((k, v) -> {
|
||||
NodeComponent nodeComponent = (NodeComponent)v.exec();
|
||||
compiledScriptMap.put(codeSpecMap.get(k), nodeComponent);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unLoad(String nodeId) {
|
||||
compiledScriptMap.remove(nodeId);
|
||||
@@ -80,12 +122,34 @@ public class JavaxProExecutor extends ScriptExecutor {
|
||||
return ScriptTypeEnum.JAVA;
|
||||
}
|
||||
|
||||
// 如果在启动时阶段,则不进行编译,只是暂存,然后由loadSecondPhase进行统一编译
|
||||
// 如果不是在启动阶段,则直接进行编译
|
||||
@Override
|
||||
public Object compile(String script) throws Exception {
|
||||
CodeSpec codeSpec = new CodeSpec(convertScript(script))
|
||||
.returnType(Object.class)
|
||||
.cached(isCache);
|
||||
return Scripts.eval(codeSpec);
|
||||
boolean startUpPhase = FlowExecutorHolder.loadInstance().getStartUpPhase().get();
|
||||
if (startUpPhase){
|
||||
return new CodeSpec(convertScript(script))
|
||||
.returnType(Object.class)
|
||||
.cached(isCache);
|
||||
}else{
|
||||
CodeSpec codeSpec = new CodeSpec(convertScript(script))
|
||||
.returnType(Object.class)
|
||||
.cached(isCache);
|
||||
return Scripts.eval(codeSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResp validate(String script){
|
||||
try {
|
||||
CodeSpec codeSpec = new CodeSpec(convertScript(script))
|
||||
.returnType(Object.class)
|
||||
.cached(isCache);
|
||||
Scripts.eval(codeSpec);
|
||||
} catch (Exception e) {
|
||||
return ValidationResp.fail(e);
|
||||
}
|
||||
return ValidationResp.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.yomahub.liteflow.test.batchLoad;
|
||||
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@TestPropertySource(value = "classpath:/common/application.properties")
|
||||
@SpringBootTest(classes = ScriptJavaxProBatchELTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({ "com.yomahub.liteflow.test.script.javaxpro.common.cmp" })
|
||||
public class ScriptJavaxProBatchELTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testCommon1() {
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
Assertions.assertTrue(response.isSuccess());
|
||||
Assertions.assertEquals(6, (int)context.getData("s1"));
|
||||
Assertions.assertEquals("hello,jack", context.getData("hi"));
|
||||
Assertions.assertEquals(47100, (Integer) context.getData("salary"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommon2() {
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
|
||||
Assertions.assertTrue(response.isSuccess());
|
||||
}
|
||||
}
|
||||
41
pom.xml
41
pom.xml
@@ -39,7 +39,7 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<revision>2.15.2</revision>
|
||||
<revision>2.15.3</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
@@ -77,7 +77,7 @@
|
||||
<redisson.version>3.21.0</redisson.version>
|
||||
<janino.version>3.1.12</janino.version>
|
||||
<kotlin.version>2.2.0</kotlin.version>
|
||||
<liquor.version>1.5.8</liquor.version>
|
||||
<liquor.version>1.6.6</liquor.version>
|
||||
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
|
||||
<sharding-jdbc.version>4.1.1</sharding-jdbc.version>
|
||||
<apache-commons-test.version>1.14.0</apache-commons-test.version>
|
||||
@@ -363,6 +363,24 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
@@ -463,25 +481,6 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
||||
Reference in New Issue
Block a user