一种接口依赖关系分层方案
来源 | OSCHINA 社区
作者 | 京东云开发者-京东零售 王江波
原文链接:https://my.oschina.net/u/4090830/blog/10085060
1、背景
原始调用方式:
2、优化
改进调用方式:
3、如何分层?
接口梳理分层流程如下:
1 首先,定义数据结构用于维护调用链路
public class DataNode {
/**
* 节点名称
*/
private String name;
/**
* 节点层级
*/
private int level;
/**
* 祖先节点
*/
private List<String> ancestors;
/**
* 子节点
*/
private List<DataNode> children;
}
2 获取能力层的接口依赖,并生成对应的数据节点
/**
* 构建层级结构
*
* @param handlers 接口依赖
* @return 数据节点集
*/
private List<DataNode> buildLevel(Set<String> handlers) {
List<DataNode> result = Lists.newArrayList();
for (String next : handlers) {
DataNode dataNode = generateNode(next, 1, null, null);
result.add(dataNode);
}
return result;
}
/**
* 生成数据节点
*
* @param name 节点名称
* @param level 节点层级
* @param ancestors 祖先节点(除父辈)
* @param parent 父节点
* @return DataNode 数据节点
*/
private DataNode generateNode(String name, int level, List<String> ancestors, String parent) {
AbstractInfraHandler abstractInfraHandler = abstractInfraHandlerMap.get(name);
Set<String> infraDependencyHandlerNames = abstractInfraHandler.getInfraDependencyHandlerNames();
// 根节点
DataNode dataNode = new DataNode(name);
dataNode.setLevel(level);
dataNode.putAncestor(ancestors, parent);
if (CollectionUtils.isNotEmpty(dataNode.getAncestors()) && dataNode.getAncestors().contains(name)) {
throw new IllegalStateException("依赖关系中存在循环依赖,请检查以下handler:" + JsonUtil.toJsonString(dataNode.getAncestors()));
}
if (CollectionUtils.isNotEmpty(infraDependencyHandlerNames)) {
// 存在子节点,子节点层级+1
for (String next : infraDependencyHandlerNames) {
DataNode child = generateNode(next, level + 1, dataNode.getAncestors(), name);
dataNode.putChild(child);
}
}
return dataNode;
}
层级结构如下:
3 数据节点平铺(遍历出所有后代节点)
Q1:如何处理接口依赖过程中的重复项?
A1:遍历所有的子节点,将所有子节点平铺到一层,平铺时如果节点已经存在,比较层级,保留层级大的即可(层级大说明依赖位于更底层,调用时要优先调用)。
/**
* 层级结构平铺
*
* @param dataNodes 数据节点
* @param dataNodeMap 平铺结构
*/
private void flatteningNodes(List<DataNode> dataNodes, Map<String, DataNode> dataNodeMap) {
if (CollectionUtils.isNotEmpty(dataNodes)) {
for (DataNode dataNode : dataNodes) {
DataNode dataNode1 = dataNodeMap.get(dataNode.getName());
if (Objects.nonNull(dataNode1)) {
// 存入层级大的即可,避免重复
if (dataNode1.getLevel() < dataNode.getLevel()) {
dataNodeMap.put(dataNode.getName(), dataNode);
}
} else {
dataNodeMap.put(dataNode.getName(), dataNode);
}
// 处理子节点
flatteningNodes(dataNode.getChildren(), dataNodeMap);
}
}
}
平铺结构如下:
4 分层(分组排序)
Q1:如何分层?
A1:节点平铺后已经去重,此时借助 TreeMap 的自然排序特性将节点按照层级分组即可。
/**
* @param dataNodeMap 平铺结构
* @return 分层结构
*/
private TreeMap<Integer, Set<DataNode>> processLevel(Map<String, DataNode> dataNodeMap) {
return dataNodeMap.values().stream().collect(Collectors.groupingBy(DataNode::getLevel, TreeMap::new, Collectors.toSet()))
}
4、分层级调用
/**
* 构建编排流程
*
* @param infraDependencyHandlers 依赖接口
* @param workerExecutor 并发线程
* @return 执行数据
*/
public Sirector<InfraContext> buildSirector(Set<String> infraDependencyHandlers, ThreadPoolExecutor workerExecutor) {
Sirector<InfraContext> sirector = new Sirector<>(workerExecutor);
long start = System.currentTimeMillis();
// 依赖顺序与执行顺序相反
TreeMap<Integer, Set<DataNode>> levelNodes;
TreeMap<Integer, Set<DataNode>> cacheLevelNodes = localCacheManager.getValue("buildSirector");
if (Objects.nonNull(cacheLevelNodes)) {
levelNodes = cacheLevelNodes;
} else {
levelNodes = getLevelNodes(infraDependencyHandlers);
ExecutorUtil.executeVoid(asyncTpExecutor, () -> localCacheManager.putValue("buildSirector", levelNodes));
}
log.info("buildSirector 梳理依赖关系耗时:{}", System.currentTimeMillis() - start);
// 最底层接口执行
Integer firstLevel = levelNodes.lastKey();
EventHandler[] beginHandlers = levelNodes.get(firstLevel).stream().map(node -> abstractInfraHandlerMap.get(node.getName())).toArray(EventHandler[]::new);
EventHandlerGroup group = sirector.begin(beginHandlers);
Integer lastLevel = levelNodes.firstKey();
for (int i = firstLevel - 1; i >= lastLevel; i--) {
EventHandler[] thenHandlers = levelNodes.get(i).stream().map(node -> abstractInfraHandlerMap.get(node.getName())).toArray(EventHandler[]::new);
group.then(thenHandlers);
}
return sirector;
}
5、 个人思考
作为接入内部 RPC、Http 接口实现业务处理的项目,在使用过程中要关注调用链路上的资源复用,尤其长链路的调用,要深入考虑内存资源的利用以及对底层服务的压力。
要关注对外服务接口与底层数据接口的响应时差,分析调用逻辑与流程是否合理,是否存在优化项。
多线程并发调用多个平行数据接口时,如何使得各个线程的耗时方差尽可能小?
2023 源创会线下重启,基础软件技术面面谈。 🕜时间:2023 年 7 月 1 日 📍地点:广东省深圳市南山区高新南四道创维半导体设计大厦裙楼四楼·SKYWORK会议中心【国际会议中心】
【嘉宾预告】
演讲人:
张钰,粤港澳大湾区数字经济研究院开发工程师,Moonbit平台核心开发人员,编程语言理论爱好者。
演讲主题:
Moonbit 编程语言平台简介
演讲大纲:
Moonbit 是由张宏波老师带领的基础软件中心团队开发的一个专为云计算、边缘计算设计的编程语言平台,这个项目包括了面向webassembly生态设计的一款应用型编程语言及其相关完整的配套工具链,本次演讲主要对会介绍这个项目动机背景、宏观方向上的设计考量以及目前的进展进行介绍,并且会有现场demo进行一些编程语言特性的演示。
👇 立即参与
往期推荐
DragGAN开源仅一天,star数超2万,史上最强AI修图工具
点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦
微信扫码关注该文公众号作者
戳这里提交新闻线索和高质量文章给我们。
来源: qq
点击查看作者最近其他文章