feature #IDGGGC Java脚本启动时批量编译,加快启动速度

This commit is contained in:
everywhere.z
2025-12-29 11:38:22 +08:00
parent d5f438ff19
commit 0f2c88b657
8 changed files with 162 additions and 35 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -70,7 +70,6 @@ public class ScriptExecutorFactory {
}
public void cleanScriptCache() {
this.scriptExecutorMap.forEach((key, value) -> value.cleanCache());
this.scriptExecutorMap.clear();
}
}

View File

@@ -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);
}
/**

View File

@@ -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>

View File

@@ -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

View File

@@ -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
View File

@@ -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>