
org.jgrapht.graph.DragService Maven / Gradle / Ivy
package org.jgrapht.graph;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.spring.boxes.dollar.JSONUtils;
import com.spring.boxes.dollar.support.MoreStream;
import org.jgrapht.graph.beans.DragonPipeline;
import org.jgrapht.graph.beans.DragonPipeline.BasePipeline;
import org.jgrapht.graph.beans.DragonPipeline.DragonOperator;
import org.jgrapht.graph.beans.DragonPipeline.PipelineManagerConfig;
import org.jgrapht.graph.beans.DagVertex;
import org.jgrapht.graph.beans.DagWeave;
import org.jgrapht.graph.dagger.VertexDataset;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DragService {
/**
* 获取DAG入口结点编号 和 出口结点编号 "只能有一个开始节点", 出口结点原则上只能有一个(许可多出口结点,串行结点1个用于最终)
* @param vertexIdMap 入度结点
* @return DAG入口结点编号
*/
private static Pair getEndpointVertexId(Map vertexIdMap) {
Set allVertexIdSet = vertexIdMap.keySet();
Set hasInDegreeNodeIdSet = new HashSet<>();
List stopNodes = Lists.newArrayList();
for (String vertexId : allVertexIdSet) {
List subProcessorIds = vertexIdMap.get(vertexId).getDownstreamProcessor();
// 统计入度顶点
if (CollectionUtils.isNotEmpty(subProcessorIds)) {
hasInDegreeNodeIdSet.addAll(subProcessorIds);
}
// 统计出度结点
if(CollectionUtils.isEmpty(subProcessorIds)){
stopNodes.add(vertexId);
}
}
// 这里认为不会出现环
List startNodes = allVertexIdSet.stream().filter(uniqueId -> !hasInDegreeNodeIdSet.contains(uniqueId)).collect(Collectors.toList());
Preconditions.checkArgument(startNodes.size() == 1, "只能有一个开始节点");
Preconditions.checkArgument(stopNodes.size() == 1, "只能有一个结束节点");
return Pair.of(startNodes.get(0), stopNodes.get(0));
}
/**
* 将配置数据转换成有向无环图
* @param dragonfly Dag编排结构
* @return
*/
public static DagWeave toDirectedAcyclicGraph(String dragonfly) {
DragonPipeline dragonPipeline = JSONUtils.fromJSON(dragonfly, DragonPipeline.class);
// 获取结点拓扑关系
Map processorMap = Optional.ofNullable(dragonPipeline)
.map(DragonPipeline::getPipelineManagerConfig)
.map(PipelineManagerConfig::getBasePipeline)
.map(BasePipeline::getProcessor)
.orElse(Maps.newHashMap());
Preconditions.checkArgument(MapUtils.isNotEmpty(processorMap), "DAG缺少执行结点(算子)");
// 获取入口&出口结点
Pair endpointVertexId = getEndpointVertexId(processorMap);
log.info("[DAG] [出/入端点] {} --> {}", endpointVertexId.getLeft(), endpointVertexId.getRight());
// 转换为有向无环图
DirectedAcyclicGraph directedAcyclicGraph = new DirectedAcyclicGraph<>(DefaultEdge.class);
// 添加所有DAG顶点
processorMap.forEach((k, v) -> {
directedAcyclicGraph.addVertex(new DagVertex(k, v.getTypeName()));
});
// 顶点辅助,For映射
Map vertexHash = MoreStream
.toMap(directedAcyclicGraph.vertexSet(), DagVertex::getVertexId, Function.identity());
// 添加DAG的边
processorMap.forEach((k, v) -> {
DagVertex sourceVertex = MapUtils.getObject(vertexHash, k);
v.getDownstreamProcessor().forEach(e -> {
DagVertex targetVertex = MapUtils.getObject(vertexHash, e);
Preconditions.checkArgument(Objects.nonNull(sourceVertex) && Objects.nonNull(targetVertex), "DAG缺少执行结点(算子)");
log.info("[DAG] [构建有向边] {} --> {}", sourceVertex.getVertexId(), targetVertex.getVertexId());
directedAcyclicGraph.addEdge(sourceVertex, targetVertex);
});
});
DagVertex startVertex = MapUtils.getObject(vertexHash, endpointVertexId.getLeft());
DagVertex stopVertex = MapUtils.getObject(vertexHash, endpointVertexId.getRight());
DagWeave dagWeave = new DagWeave();
dagWeave.setVertexHash(vertexHash);
dagWeave.setStartVertex(startVertex);
dagWeave.setStopVertex(stopVertex);
dagWeave.setDirectedAcyclicGraph(directedAcyclicGraph);
return dagWeave;
}
public static void main(String[] args) {
String source = "{\n"
+ " \"pipeline_manager_config\": {\n"
+ " \"base_pipeline\": {\n"
+ " \"processor\": {\n"
+ " \"resourceAbTestInstanceWorker_5\": {\n"
+ " \"type_name\": \"resourceAbTestInstanceWorker\",\n"
+ " \"downstream_processor\": [\n"
+ " \"resourcePendantBusinessDataWorker_4\",\n"
+ " \"resourceAbTestInstanceWorker_3\"\n"
+ " ],\n"
+ " \"config\": [\n"
+ " {\n"
+ " \"field\": \"name\",\n"
+ " \"labelText\": \"节点名称\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"input\",\n"
+ " \"value\": \"resourceAbTestInstanceWorker_5\",\n"
+ " \"required\": true\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abBizName\",\n"
+ " \"labelText\": \"abBizName\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"ab所属业务域\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abParamType\",\n"
+ " \"labelText\": \"abParamType\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入ab参数类型(boolean/string/int/long/double)\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abTestParamKey\",\n"
+ " \"labelText\": \"abTestParamKey\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入ab的参数名\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abReturnValueToBranch\",\n"
+ " \"labelText\": \"abReturnValueToBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"map结构:ab返回的值对应的分支\",\n"
+ " \"required\": false\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"resourceAbTestInstanceWorker_3\": {\n"
+ " \"type_name\": \"resourceAbTestInstanceWorker\",\n"
+ " \"downstream_processor\": [\n"
+ " \"resourceVisitorPortraitCheckWorker_6\"\n"
+ " ],\n"
+ " \"config\": [\n"
+ " {\n"
+ " \"field\": \"name\",\n"
+ " \"labelText\": \"节点名称\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"input\",\n"
+ " \"value\": \"resourceAbTestInstanceWorker_3\",\n"
+ " \"required\": true\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abBizName\",\n"
+ " \"labelText\": \"abBizName\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"ab所属业务域\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abParamType\",\n"
+ " \"labelText\": \"abParamType\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入ab参数类型(boolean/string/int/long/double)\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abTestParamKey\",\n"
+ " \"labelText\": \"abTestParamKey\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入ab的参数名\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abReturnValueToBranch\",\n"
+ " \"labelText\": \"abReturnValueToBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"map结构:ab返回的值对应的分支\",\n"
+ " \"required\": false\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"resourceVisitorPortraitCheckWorker_2\": {\n"
+ " \"type_name\": \"resourceVisitorPortraitCheckWorker\",\n"
+ " \"downstream_processor\": [\n"
+ " \"resourceAbTestInstanceWorker_5\"\n"
+ " ],\n"
+ " \"config\": [\n"
+ " {\n"
+ " \"field\": \"name\",\n"
+ " \"labelText\": \"节点名称\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"input\",\n"
+ " \"value\": \"resourceVisitorPortraitCheckWorker_2\",\n"
+ " \"required\": true\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"blackUserList\",\n"
+ " \"labelText\": \"blackUserList\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入屏蔽用户ID,并用半角逗号隔开\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"blackUserBranch\",\n"
+ " \"labelText\": \"blackUserBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"黑名单的流转分支\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"whiteUserList\",\n"
+ " \"labelText\": \"whiteUserList\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入用户白名单ID,并用半角逗号隔开\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"whiteUserBranch\",\n"
+ " \"labelText\": \"whiteUserBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"白名单的流转分支\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"portraitToBranch\",\n"
+ " \"labelText\": \"portraitToBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"用户人群/画像分支等(json结构)\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"defaultBranch\",\n"
+ " \"labelText\": \"defaultBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"默认的流转\",\n"
+ " \"required\": false\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"resourceAbTestInstanceWorker_1\": {\n"
+ " \"type_name\": \"resourceAbTestInstanceWorker\",\n"
+ " \"downstream_processor\": [\n"
+ " \"resourceVisitorPortraitCheckWorker_2\",\n"
+ " \"resourceAbTestInstanceWorker_3\"\n"
+ " ],\n"
+ " \"config\": [\n"
+ " {\n"
+ " \"field\": \"name\",\n"
+ " \"labelText\": \"节点名称\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"input\",\n"
+ " \"value\": \"resourceAbTestInstanceWorker_1\",\n"
+ " \"required\": true\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abBizName\",\n"
+ " \"labelText\": \"abBizName\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"ab所属业务域\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abParamType\",\n"
+ " \"labelText\": \"abParamType\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入ab参数类型(boolean/string/int/long/double)\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abTestParamKey\",\n"
+ " \"labelText\": \"abTestParamKey\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入ab的参数名\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"abReturnValueToBranch\",\n"
+ " \"labelText\": \"abReturnValueToBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"map结构:ab返回的值对应的分支\",\n"
+ " \"required\": false\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"resourceVisitorPortraitCheckWorker_6\": {\n"
+ " \"type_name\": \"resourceVisitorPortraitCheckWorker\",\n"
+ " \"downstream_processor\": [\n"
+ " \"resourcePendantBusinessDataWorker_4\"\n"
+ " ],\n"
+ " \"config\": [\n"
+ " {\n"
+ " \"field\": \"name\",\n"
+ " \"labelText\": \"节点名称\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"input\",\n"
+ " \"value\": \"resourceVisitorPortraitCheckWorker_6\",\n"
+ " \"required\": true\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"blackUserList\",\n"
+ " \"labelText\": \"blackUserList\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入屏蔽用户ID,并用半角逗号隔开\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"blackUserBranch\",\n"
+ " \"labelText\": \"blackUserBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"黑名单的流转分支\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"whiteUserList\",\n"
+ " \"labelText\": \"whiteUserList\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"请输入用户白名单ID,并用半角逗号隔开\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"whiteUserBranch\",\n"
+ " \"labelText\": \"whiteUserBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"白名单的流转分支\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"portraitToBranch\",\n"
+ " \"labelText\": \"portraitToBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"用户人群/画像分支等(json结构)\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"defaultBranch\",\n"
+ " \"labelText\": \"defaultBranch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"默认的流转\",\n"
+ " \"required\": false\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"resourcePendantBusinessDataWorker_4\": {\n"
+ " \"type_name\": \"resourcePendantBusinessDataWorker\",\n"
+ " \"downstream_processor\": [\n"
+ " \n"
+ " ],\n"
+ " \"config\": [\n"
+ " {\n"
+ " \"field\": \"name\",\n"
+ " \"labelText\": \"节点名称\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"input\",\n"
+ " \"value\": \"resourcePendantBusinessDataWorker_4\",\n"
+ " \"required\": true\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"function\",\n"
+ " \"labelText\": \"function\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"功能描述\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"switch\",\n"
+ " \"labelText\": \"switch\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"true/false\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"relationBranchName\",\n"
+ " \"labelText\": \"relationBranchName\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"关联的分支名(可以不填)\",\n"
+ " \"required\": false\n"
+ " },\n"
+ " {\n"
+ " \"field\": \"pendantBusinessData\",\n"
+ " \"labelText\": \"pendantBusinessData\",\n"
+ " \"errorMessage\": \"请输入\",\n"
+ " \"type\": \"\",\n"
+ " \"value\": \"挂件业务数据(json格式)\",\n"
+ " \"required\": false\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
DagWeave dagWeave = toDirectedAcyclicGraph(source);
// System.out.println(dagWeave.getDirectedAcyclicGraph());
Map context = Maps.newHashMap();
context.put("path", 0);
VertexDataset vertexDataset = DagTemplate.dataset(dagWeave, context);
System.out.println("[场景1]执行结果:" + JSONUtils.toJSON(vertexDataset));
System.out.println("------");
Map context2 = Maps.newHashMap();
context2.put("path", 1);
VertexDataset vertexDataset2 = DagTemplate.dataset(dagWeave, context2);
System.out.println("[场景2]执行结果:" + JSONUtils.toJSON(vertexDataset2));
System.out.println("------");
Map context3 = Maps.newHashMap();
context2.put("path", 3);
VertexDataset vertexDataset3 = DagTemplate.dataset(dagWeave, context3);
System.out.println("[场景3]执行结果:" + JSONUtils.toJSON(vertexDataset3));
// vertexExecutor.setApplicationContext();
// executeDirectedAcyclicGraph(dagWeave, Maps.newHashMap());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy