mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-05-14 12:12:08 +08:00
feature #IAY66T 添加Solon环境测试
This commit is contained in:
@@ -51,6 +51,8 @@ public class LiteflowAutoConfiguration {
|
|||||||
liteflowConfig.setGlobalThreadPoolQueueSize(property.getGlobalThreadPoolQueueSize());
|
liteflowConfig.setGlobalThreadPoolQueueSize(property.getGlobalThreadPoolQueueSize());
|
||||||
liteflowConfig.setWhenThreadPoolIsolate(property.getWhenThreadPoolIsolate());
|
liteflowConfig.setWhenThreadPoolIsolate(property.getWhenThreadPoolIsolate());
|
||||||
liteflowConfig.setEnableNodeInstanceId(property.isEnableNodeInstanceId());
|
liteflowConfig.setEnableNodeInstanceId(property.isEnableNodeInstanceId());
|
||||||
|
liteflowConfig.setEnableRuleCache(property.getRuleCache().getEnabled());
|
||||||
|
liteflowConfig.setRuleCacheCapacity(property.getRuleCache().getCapacity());
|
||||||
return liteflowConfig;
|
return liteflowConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,6 +101,33 @@ public class LiteflowProperty {
|
|||||||
//是否启用节点实例ID
|
//是否启用节点实例ID
|
||||||
private boolean enableNodeInstanceId;
|
private boolean enableNodeInstanceId;
|
||||||
|
|
||||||
|
// 规则缓存配置
|
||||||
|
private RuleCache ruleCache;
|
||||||
|
|
||||||
|
public static class RuleCache {
|
||||||
|
// 是否启用规则缓存
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
// 规则缓存容量
|
||||||
|
private Integer capacity;
|
||||||
|
|
||||||
|
public Boolean getEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCapacity() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCapacity(Integer capacity) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isEnable() {
|
public boolean isEnable() {
|
||||||
return enable;
|
return enable;
|
||||||
}
|
}
|
||||||
@@ -341,4 +368,12 @@ public class LiteflowProperty {
|
|||||||
public void setEnableNodeInstanceId(boolean enableNodeInstanceId) {
|
public void setEnableNodeInstanceId(boolean enableNodeInstanceId) {
|
||||||
this.enableNodeInstanceId = enableNodeInstanceId;
|
this.enableNodeInstanceId = enableNodeInstanceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RuleCache getRuleCache() {
|
||||||
|
return ruleCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRuleCache(RuleCache ruleCache) {
|
||||||
|
this.ruleCache = ruleCache;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,3 +19,4 @@ liteflow.global-thread-pool-size=16
|
|||||||
liteflow.global-thread-pool-queue-size=512
|
liteflow.global-thread-pool-queue-size=512
|
||||||
liteflow.global-thread-pool-executor-class=com.yomahub.liteflow.thread.LiteFlowDefaultGlobalExecutorBuilder
|
liteflow.global-thread-pool-executor-class=com.yomahub.liteflow.thread.LiteFlowDefaultGlobalExecutorBuilder
|
||||||
liteflow.enable-node-instance-id=true
|
liteflow.enable-node-instance-id=true
|
||||||
|
liteflow.rule-cache.enabled=false
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package com.yomahub.liteflow.test.ruleCache;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
|
||||||
|
import com.yomahub.liteflow.core.FlowExecutor;
|
||||||
|
import com.yomahub.liteflow.exception.ChainNotFoundException;
|
||||||
|
import com.yomahub.liteflow.flow.FlowBus;
|
||||||
|
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||||
|
import com.yomahub.liteflow.flow.element.Chain;
|
||||||
|
import com.yomahub.liteflow.flow.element.Condition;
|
||||||
|
import com.yomahub.liteflow.lifecycle.LifeCycleHolder;
|
||||||
|
import com.yomahub.liteflow.lifecycle.PostProcessFlowExecuteLifeCycle;
|
||||||
|
import com.yomahub.liteflow.lifecycle.impl.RuleCacheLifeCycle;
|
||||||
|
import com.yomahub.liteflow.test.BaseTest;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.noear.solon.annotation.Import;
|
||||||
|
import org.noear.solon.annotation.Inject;
|
||||||
|
import org.noear.solon.test.SolonTest;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solon环境下规则缓存测试
|
||||||
|
* @author DaleLee
|
||||||
|
* @since 2.13.0
|
||||||
|
*/
|
||||||
|
@SolonTest
|
||||||
|
@Import(profiles="classpath:/ruleCache/application.properties")
|
||||||
|
public class RuleCacheSolonTest extends BaseTest {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private FlowExecutor flowExecutor;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void reload() {
|
||||||
|
flowExecutor.reloadRule();
|
||||||
|
// 清空缓存
|
||||||
|
Cache<String, Object> cache = getCache();
|
||||||
|
cache.invalidateAll();
|
||||||
|
cache.cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试chain被淘汰
|
||||||
|
@Test
|
||||||
|
public void testRuleCache1() {
|
||||||
|
// 加满缓存
|
||||||
|
loadCache();
|
||||||
|
// 缓存快照
|
||||||
|
HashSet<@NonNull String> strings = CollUtil.newHashSet(getCache().asMap().keySet());
|
||||||
|
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
|
||||||
|
Assertions.assertTrue(response.isSuccess());
|
||||||
|
Assertions.assertEquals("c==>b", response.getExecuteStepStr());
|
||||||
|
// 获得被淘汰chain
|
||||||
|
String chainId = getEvictedChain(strings);
|
||||||
|
testEvicted(chainId);
|
||||||
|
// 测试被淘汰的chain仍可正常执行
|
||||||
|
response = flowExecutor.execute2Resp(chainId, "arg");
|
||||||
|
Assertions.assertTrue(response.isSuccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试缓存数量
|
||||||
|
@Test
|
||||||
|
public void testRuleCache2() {
|
||||||
|
// 确保至少执行过5个不同的chain
|
||||||
|
loadCache();
|
||||||
|
// 随机执行chain
|
||||||
|
loadCache(100);
|
||||||
|
// 等待缓存淘汰
|
||||||
|
getCache().cleanUp();
|
||||||
|
// 测试只有5个chain被编译
|
||||||
|
int count = 0;
|
||||||
|
for (Chain chain : FlowBus.getChainMap().values()) {
|
||||||
|
List<Condition> conditionList = chain.getConditionList();
|
||||||
|
if (chain.isCompiled()) {
|
||||||
|
Assertions.assertTrue(CollUtil.isNotEmpty(conditionList));
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
Assertions.assertNull(conditionList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assertions.assertEquals(5, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试开启规则缓存后,进入缓存的chain可以正常被更新
|
||||||
|
@Test
|
||||||
|
public void testRuleCache3() {
|
||||||
|
loadCache();
|
||||||
|
// 缓存快照
|
||||||
|
HashSet<@NonNull String> strings = CollUtil.newHashSet(getCache().asMap().keySet());
|
||||||
|
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
|
||||||
|
Assertions.assertTrue(response.isSuccess());
|
||||||
|
Assertions.assertEquals("x==>a==>b", response.getExecuteStepStr());
|
||||||
|
// chain7进入缓存
|
||||||
|
Assertions.assertTrue(getCache().asMap().containsKey("chain7"));
|
||||||
|
// 获得被淘汰chain
|
||||||
|
String chainId = getEvictedChain(strings);
|
||||||
|
testEvicted(chainId);
|
||||||
|
// 更新chain7
|
||||||
|
LiteFlowChainELBuilder
|
||||||
|
.createChain()
|
||||||
|
.setChainId("chain7")
|
||||||
|
.setEL("THEN(a, b, c)")
|
||||||
|
.build();
|
||||||
|
// 重新执行chain7
|
||||||
|
response = flowExecutor.execute2Resp("chain7", "arg");
|
||||||
|
Assertions.assertTrue(response.isSuccess());
|
||||||
|
Assertions.assertEquals("a==>b==>c", response.getExecuteStepStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试开启规则缓存后,进入缓存的chain被移除后无法执行
|
||||||
|
@Test
|
||||||
|
public void testRuleCache4() {
|
||||||
|
loadCache();
|
||||||
|
// 缓存快照
|
||||||
|
HashSet<@NonNull String> strings = CollUtil.newHashSet(getCache().asMap().keySet());
|
||||||
|
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
|
||||||
|
Assertions.assertTrue(response.isSuccess());
|
||||||
|
Assertions.assertEquals("x==>a==>b", response.getExecuteStepStr());
|
||||||
|
// chain7进入缓存
|
||||||
|
Assertions.assertTrue(getCache().asMap().containsKey("chain7"));
|
||||||
|
// 获得被淘汰chain
|
||||||
|
String chainId = getEvictedChain(strings);
|
||||||
|
testEvicted(chainId);
|
||||||
|
// 手动移除chain7
|
||||||
|
FlowBus.removeChain("chain7");
|
||||||
|
response = flowExecutor.execute2Resp("chain7", "arg");
|
||||||
|
Assertions.assertFalse(response.isSuccess());
|
||||||
|
Assertions.assertEquals(ChainNotFoundException.class, response.getCause().getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试并发下,正在执行的chain被淘汰仍能执行
|
||||||
|
@Test
|
||||||
|
public void testRuleCache5() throws InterruptedException {
|
||||||
|
// 模拟清空编译好的chain
|
||||||
|
Thread thread = new Thread(()-> {
|
||||||
|
Chain chain1 = FlowBus.getChain("chain1");
|
||||||
|
chain1.setCompiled(true);
|
||||||
|
chain1.setConditionList(null);
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
thread.join();
|
||||||
|
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||||
|
Assertions.assertTrue(response.isSuccess());
|
||||||
|
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 加载缓存, chain1~chain5
|
||||||
|
private void loadCache() {
|
||||||
|
// 容量上限为5
|
||||||
|
for (int i = 1; i <= 5; i++) {
|
||||||
|
flowExecutor.execute2Resp("chain" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCache(int count) {
|
||||||
|
// 随机执行chain
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int id = random.nextInt(10) + 1;
|
||||||
|
flowExecutor.execute2Resp("chain" + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试 chain 被淘汰
|
||||||
|
private void testEvicted(String chanId) {
|
||||||
|
Chain chain = FlowBus.getChain(chanId);
|
||||||
|
getCache().cleanUp();
|
||||||
|
// 测试缓存中不存在
|
||||||
|
Assertions.assertFalse(getCache().asMap().containsKey(chanId));
|
||||||
|
// 测试chain被设置为未编译
|
||||||
|
Assertions.assertFalse(chain.isCompiled());
|
||||||
|
Assertions.assertNull(chain.getConditionList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cache<String, Object> getCache() {
|
||||||
|
List<PostProcessFlowExecuteLifeCycle> lifeCycleList
|
||||||
|
= LifeCycleHolder.getPostProcessFlowExecuteLifeCycleList();
|
||||||
|
for (PostProcessFlowExecuteLifeCycle lifeCycle : lifeCycleList) {
|
||||||
|
if (lifeCycle.getClass().equals(RuleCacheLifeCycle.class)) {
|
||||||
|
RuleCacheLifeCycle ruleCacheLifeCycle = (RuleCacheLifeCycle) lifeCycle;
|
||||||
|
return ruleCacheLifeCycle.getCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得淘汰的chain,传入淘汰前的chain集合
|
||||||
|
// 确保只有一个被淘汰时使用
|
||||||
|
String getEvictedChain(Set<String> set) {
|
||||||
|
Cache<String, Object> cache = getCache();
|
||||||
|
cache.cleanUp();
|
||||||
|
Set<@NonNull String> strings = cache.asMap().keySet();
|
||||||
|
set.removeAll(strings);
|
||||||
|
Assertions.assertEquals(1, set.size());
|
||||||
|
return set.iterator().next();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* <p>Title: liteflow</p>
|
||||||
|
* <p>Description: 轻量级的组件式流程框架</p>
|
||||||
|
* @author Bryan.Zhang
|
||||||
|
* @email weenyc31@163.com
|
||||||
|
* @Date 2020/4/1
|
||||||
|
*/
|
||||||
|
package com.yomahub.liteflow.test.ruleCache.cmp;
|
||||||
|
|
||||||
|
import com.yomahub.liteflow.core.NodeComponent;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
|
||||||
|
@Component("a")
|
||||||
|
public class ACmp extends NodeComponent {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() {
|
||||||
|
System.out.println("ACmp executed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* <p>Title: liteflow</p>
|
||||||
|
* <p>Description: 轻量级的组件式流程框架</p>
|
||||||
|
* @author Bryan.Zhang
|
||||||
|
* @email weenyc31@163.com
|
||||||
|
* @Date 2020/4/1
|
||||||
|
*/
|
||||||
|
package com.yomahub.liteflow.test.ruleCache.cmp;
|
||||||
|
|
||||||
|
import com.yomahub.liteflow.core.NodeComponent;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
|
||||||
|
@Component("b")
|
||||||
|
public class BCmp extends NodeComponent {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() {
|
||||||
|
System.out.println("BCmp executed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* <p>Title: liteflow</p>
|
||||||
|
* <p>Description: 轻量级的组件式流程框架</p>
|
||||||
|
* @author Bryan.Zhang
|
||||||
|
* @email weenyc31@163.com
|
||||||
|
* @Date 2020/4/1
|
||||||
|
*/
|
||||||
|
package com.yomahub.liteflow.test.ruleCache.cmp;
|
||||||
|
|
||||||
|
import com.yomahub.liteflow.core.NodeComponent;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
|
||||||
|
@Component("c")
|
||||||
|
public class CCmp extends NodeComponent {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() {
|
||||||
|
System.out.println("CCmp executed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.yomahub.liteflow.test.ruleCache.cmp;
|
||||||
|
|
||||||
|
import com.yomahub.liteflow.core.NodeBooleanComponent;
|
||||||
|
import org.noear.solon.annotation.Component;
|
||||||
|
|
||||||
|
@Component("x")
|
||||||
|
public class XCmp extends NodeBooleanComponent {
|
||||||
|
@Override
|
||||||
|
public boolean processBoolean() throws Exception {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
liteflow.rule-source=ruleCache/flow.el.xml
|
||||||
|
liteflow.rule-cache.enabled=true
|
||||||
|
liteflow.rule-cache.capacity=5
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<flow>
|
||||||
|
<chain name="chain1">
|
||||||
|
THEN(a, b);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain2">
|
||||||
|
THEN(a, c);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain3">
|
||||||
|
THEN(b, a);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain4">
|
||||||
|
THEN(b, c);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain5">
|
||||||
|
THEN(c, a);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain6">
|
||||||
|
THEN(c, b);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain7">
|
||||||
|
THEN(IF(x, a), b);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain8">
|
||||||
|
THEN(IF(x, a), c);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain9">
|
||||||
|
WHEN(a, b, c);
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain10">
|
||||||
|
FOR(5).DO(THEN(a, b, c));
|
||||||
|
</chain>
|
||||||
|
|
||||||
|
</flow>
|
||||||
Reference in New Issue
Block a user