在微服务架构下,服务之间的关系是非常复杂的,是一个典型的有向有环图,在一个中等规模的项目中,一般会有100多个服务,而大型项目中,则会有数百个服务。
假设我们有如下6个服务:
每个服务都指定了自己依赖的服务:
AaaSvc:
BbbSvc:
CccSvc:
DddSvc:
EeeSvc:
FffSvc:
我们如何把如上6个服务中跟服务AaaSvc相关的服务可视化呢?如下图所示:
要完成这样的服务关联图,需要如下几个步骤:
1、遍历指定项目下的所有服务,构造两个map,serviceToServiceMap 和 reverseServiceToServiceMap,存储所有服务的直接依赖和反向直接依赖。
public static void runService(String projectId, Map> serviceToServiceMap, Map > reverseServiceToServiceMap){ if(! (serviceToServiceMap instanceof ConcurrentHashMap) ){ throw new RuntimeException("参数serviceToServiceMap必须是ConcurrentHashMap的实例"); } if(! (reverseServiceToServiceMap instanceof ConcurrentHashMap) ){ throw new RuntimeException("参数reverseServiceToServiceMap必须是ConcurrentHashMap的实例"); } MetaServiceRepository metaServiceRepository = SpringContextUtils.getBean("metaServiceRepository"); List services = metaServiceRepository.findByProjectId(projectId); services.parallelStream().filter(item->!item.getName().contains("Deprecate")).forEach(item->{ List dependencyServices = item.constructDependencyServices(); String key = item.getName()+"("+item.getDescription()+")"; if(dependencyServices != null){ dependencyServices.parallelStream().filter(dep->!dep.getName().contains("Deprecate")).forEach(dependencyService->{ String value = dependencyService.getName()+"("+dependencyService.getDescription()+")"; serviceToServiceMap.putIfAbsent(key, Collections.newSetFromMap(new ConcurrentHashMap<>())); serviceToServiceMap.get(key).add(value); reverseServiceToServiceMap.putIfAbsent(value, Collections.newSetFromMap(new ConcurrentHashMap<>())); reverseServiceToServiceMap.get(value).add(key); }); } }); }
2、以服务AaaSvc为入口,利用直接依赖和反向直接依赖,构造服务依赖图和反向服务依赖图。
String name = metaService.getName()+"("+metaService.getDescription()+")";Setset = serviceToServiceMap.get(name);ServiceDependencyGraph serviceDependencyGraph = new ServiceDependencyGraph(new HashMap<>(), name, set, serviceToServiceMap);set = reverseServiceToServiceMap.get(name);ServiceDependencyGraph reverseServiceDependencyGraph = new ServiceDependencyGraph(new HashMap<>(), name, set, reverseServiceToServiceMap);
import org.apache.commons.collections4.CollectionUtils;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.atomic.AtomicInteger;/** * 服务依赖图 * @date 2017-09-2 * @author 杨尚川 */public class ServiceDependencyGraph { private String name; private Listchildren = new ArrayList<>(); public ServiceDependencyGraph(Map stopServiceNames, String name, Set set, Map > serviceToServiceMap){ this.name = name; if(CollectionUtils.isNotEmpty(set)) { for (String item : set) { String key = name+"_"+item; stopServiceNames.putIfAbsent(key, new AtomicInteger()); stopServiceNames.get(key).incrementAndGet(); if(stopServiceNames.get(key).get()<10) { Set sub = serviceToServiceMap.get(item); ServiceDependencyGraph serviceDependencyGraph = new ServiceDependencyGraph(stopServiceNames, item, sub, serviceToServiceMap); children.add(serviceDependencyGraph); } } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getChildren() { return children; }}
3、利用服务依赖图和反向服务依赖图,构造带有点和边描述信息的JSON结构:
List
private void addNode(String node, List> nodes){ for(Map item : nodes){ if(node.equals(item.get("id"))){ return; } } Map nodeMap = new HashMap<>(); nodeMap.put("id", node); nodeMap.put("name", node); nodes.add(nodeMap);}private void addNode(ServiceDependencyGraph serviceDependencyGraph, List > nodes){ if(serviceDependencyGraph == null){ return; } String node = serviceDependencyGraph.getName(); addNode(node, nodes); if(serviceDependencyGraph.getChildren() != null){ serviceDependencyGraph.getChildren().forEach(item->addNode(item, nodes)); }}private void addEdge(Map > reverseServiceToServiceMap, ServiceDependencyGraph serviceDependencyGraph, List > edges, boolean reverse){ if(serviceDependencyGraph == null){ return; } String source = serviceDependencyGraph.getName(); serviceDependencyGraph.getChildren().forEach(target -> { boolean duplicate = false; Map map = new HashMap<>(); if(reverse){ String id = target.getName()+"-->"+source; for(Map item : edges){ if(id.equals(item.get("id"))){ duplicate = true; } } map.put("id", id); map.put("target", source); map.put("source", target.getName()); map.put("directed", true); map.put("source_score", reverseServiceToServiceMap.get(target.getName()) == null ? 0 : reverseServiceToServiceMap.get(target.getName()).size()); map.put("target_score", reverseServiceToServiceMap.get(source) == null ? 0 : reverseServiceToServiceMap.get(source).size()); }else { String id = source+"-->"+target.getName(); for(Map item : edges){ if(id.equals(item.get("id"))){ duplicate = true; } } map.put("id", id); map.put("source", source); map.put("target", target.getName()); map.put("directed", true); map.put("source_score", reverseServiceToServiceMap.get(source) == null ? 0 : reverseServiceToServiceMap.get(source).size()); map.put("target_score", reverseServiceToServiceMap.get(target.getName()) == null ? 0 : reverseServiceToServiceMap.get(target.getName()).size()); } if(!duplicate) { edges.add(map); } addEdge(reverseServiceToServiceMap, target, edges, reverse); });}
生成的JSON结构如下所示:
{ "nodes":[ { "globalWeight":4, "name":"AaaSvc(服务Aaa)" }, { "globalWeight":4, "name":"CccSvc(服务Ccc)" }, { "globalWeight":5, "name":"DddSvc(服务Ddd)" }, { "globalWeight":4, "name":"EeeSvc(服务Eee)" }, { "globalWeight":4, "name":"FffSvc(服务Fff)" }, { "globalWeight":3, "name":"BbbSvc(服务Bbb)" } ], "edges":[ { "distance":8, "source":0, "target":1 }, { "distance":8, "source":1, "target":0 }, { "distance":9, "source":0, "target":2 }, { "distance":9, "source":2, "target":3 }, { "distance":9, "source":3, "target":2 }, { "distance":9, "source":2, "target":4 }, { "distance":8, "source":4, "target":3 }, { "distance":8, "source":3, "target":4 }, { "distance":9, "source":4, "target":2 }, { "distance":7, "source":0, "target":5 }, { "distance":7, "source":5, "target":1 }, { "distance":7, "source":1, "target":5 } ]}
4、使用d3-force对如上的JSON进行展示:
测试项目_testDemo -- 服务Aaa_AaaSvc -- 服务关联图 测试项目_testDemo -- 服务Aaa_AaaSvc -- 服务关联图
重力调节 | 最短边长 | 边长放大倍数 | 最短半径 | 半径放大倍数 | 箭头大小 |
最终运行的效果如下: