品牌 资讯 搭配 材料 时尚 热点 行业 首饰 玉石 行情

一种接口依赖关系分层方案|快看

2023-06-27 10:26:55 来源:博客园
1、背景

到店商详迭代过程中,需要提供的对外能力越来越多,如预约日历、附近门店、为你推荐等。这其中不可避免会出现多个上层能力依赖同一个底层接口的场景。最初采用的方案是对外API入口进来后获取对应的能力,并发调用多项能力,由能力层调用对应的数据链路,进行业务处理。然而,随着接入功能的增多,这种情况导致了底层数据服务的重复调用,如商品配置信息,在一次API调用过程中重复调了3次,当流量增大或能力项愈多时,对底层服务的压力会成倍增加。

正值618大促,各方接口的调用都会大幅度增加。通过梳理接口依赖关系来减少重复调用,对本系统而言,降低了调用数据接口时的线程占用次数,可以有效降级CPU。对调用方来说,减少了调用次数,可减少调用方的资源消耗,保障底层服务的稳定性。

原始调用方式:2、优化

基于上述问题,采用底层接口依赖分层调用的方案。梳理接口依赖关系,逐层向上调用,注入数据,如此将同一接口的调用抽取到某层,仅调用一次,即可在整条链路使用。


(相关资料图)

改进调用方式:

只要分层后即可在每层采用多线程并发的方式调用,因为同一层级中的接口无先后依赖关系。

3、如何分层?

接下来,如何梳理接口层级关系就至关重要。

接口梳理分层流程如下:

第一步:构建层级结构

首先获取到能力层依赖项并遍历,然后调用生成数据节点方法。方法流程如下:构建当前节点,检测循环依赖(存在循环依赖会导致栈溢出),获取并遍历节点依赖项,递归生成子节点,存放子节点。

第二步:节点平铺

定义Map维护平铺结构,调用平铺方法。方法流程如下:遍历层级结构,判断当前节点是否已存在map中,存在时与原节点比较将层级大的节点放入(去除重复项),不存在时直接放入即可。然后处理子节点,递归调用平铺方法,处理所有节点。

第三步:分层(分组排序)

流处理平铺结构,处理层级分组,存储在TreeMap中维护自然排序。对应key中的数据节点Set需用多线程并发调用,以保证链路调用时间

1 首先,定义数据结构用于维护调用链路

Q1:为什么需要定义祖先节点?

A1:为了判断接口是否存在循环依赖。如果接口存在循环依赖而不检测将导致调用栈溢出,故而在调用过程中要避免并检测循环依赖。在遍历子节点过程中,如果发现当前节点的祖先已经包含当前子节点,说明依赖关系出现了环路,即循环依赖,此时抛异常终止后续流程避免栈溢出。

public class DataNode {    /**     * 节点名称     */    private String name;    /**     * 节点层级     */    private int level;    /**     * 祖先节点     */    private List ancestors;    /**     * 子节点     */    private List children;}
2 获取能力层的接口依赖,并生成对应的数据节点

Q1:生成节点时如何维护层级?

A1:从能力层依赖开始,层级从1递加。每获取一次底层依赖,底层依赖所生成的节点层级即父节点层级+1。

/** * 构建层级结构 * * @param handlers 接口依赖 * @return 数据节点集 */private List buildLevel(Set handlers) {    List 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 ancestors, String parent) {    AbstractInfraHandler abstractInfraHandler = abstractInfraHandlerMap.get(name);    Set 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 dataNodes, Map 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> processLevel(Map dataNodeMap) {    return dataNodeMap.values().stream().collect(Collectors.groupingBy(DataNode::getLevel, TreeMap::new, Collectors.toSet()))}

分层如下:

1.根据分层TreeMap的key倒序即为调用的层级顺序

对应key中的数据节点Set需用多线程并发调用,以保证链路调用时间

4、分层级调用

梳理出调用关系并分层后,使用并发编排工具调用即可。这里梳理的层级关系,level越大,表示越优先调用。

这里以京东内部并发编排框架为例,说明调用流程:

/** * 构建编排流程 * * @param infraDependencyHandlers 依赖接口 * @param workerExecutor 并发线程 * @return 执行数据 */public Sirector buildSirector(Set infraDependencyHandlers, ThreadPoolExecutor workerExecutor) {    Sirector sirector = new Sirector<>(workerExecutor);    long start = System.currentTimeMillis();    // 依赖顺序与执行顺序相反    TreeMap> levelNodes;    TreeMap> 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接口实现业务处理的项目,在使用过程中要关注调用链路上的资源复用,尤其长链路的调用,要深入考虑内存资源的利用以及对底层服务的压力。

要关注对外服务接口与底层数据接口的响应时差,分析调用逻辑与流程是否合理,是否存在优化项。

多线程并发调用多个平行数据接口时,如何使得各个线程的耗时方差尽可能小?

作者:京东零售 王江波

来源:京东云开发者社区

标签:

(责任编辑:)

相关文章

摩洛哥地震遇难人数已升至632人,还有329人受伤

​摩洛哥强震更新:据摩洛哥媒体报道,摩洛哥地震遇难人数已升至632人,

2023-09-09 14:49:45

1983年4月,挪威海军对外宣称,他们决定将那艘侵入领海的潜艇彻

​1983年4月,挪威海军对外宣称,他们决定将那艘侵入领海的潜艇彻底击

2023-09-09 12:45:40

摩洛哥地震已致296人死亡,另有153人不同程度受伤

​据摩洛哥媒体当地时间9日报道,摩洛哥皇家武装部队援引内政部消息称,

2023-09-09 10:42:14

外资疯狂撤离新台币又贬 最低创十个月新低

​新台币昨日贬破32元大关,今日美元续强、台股下跌使得新台币贬势未歇,

2023-09-09 09:08:29

2023年09月09日(10至14公斤)仔猪价格行情走势

​2023年09月09日(10至14公斤)仔猪价格行情走势报价,单位:元 公斤河北

2023-09-09 07:42:12

淋浴房玻璃水垢清除妙方(淋浴房玻璃上的水垢清洗妙招)

​1、可用醋和少许食盐的混合液,即可去掉。2、也可以选用牙膏将浴室的玻

2023-09-09 04:09:50

时政微视频丨瞰黑龙江

​五光山色翠影满湖    冰雪分辉林深鹿鸣    沃土锦粮日华霞灿 

2023-09-08 22:37:34

货物物流 货运平台)

​王老师先不要管堂食问题了,谈谈物流梗阻问题吧!我在拼多多买十几件商

2023-09-08 21:11:01

今日待机时间长的安卓手机排名(有没有待机时间长的安卓手机)

​您好,来为大家解答以上问题。待机时间长的安卓手机排名,有没有待机时

2023-09-08 19:57:14

象冢课文 象冢

​1、xiang四声zhong三声意思是大象的坟墓。相信通过象冢这篇文章能帮到

2023-09-08 19:14:46

2023年9月8日上海市异氟尔酮价格最新行情预测

​中国报告大厅2023年9月8日上海市异氟尔酮价格最新走势监测显示:武汉恒

2023-09-08 18:03:08

交汇点调查|什么?新疆也产大螃蟹?还打进了江苏市场?背后究竟有怎样的故事

​“这不是开玩笑!新疆的螃蟹卖到江苏了!”9月6日,一则视频刷爆网络。

2023-09-08 16:51:32

吉县农商银行:红苹果挑起服务三农的金扁担

​作为我国苹果最佳优生区之一的吉县,这儿的苹果“好看、好吃、好存”,

2023-09-08 15:51:21

江西省交通运输执法局:小型船舶检验新模式让便民服务更高效(图)

​“一次都不用跑,证书就给我们寄到家里来了,现在小型船舶检验真是

2023-09-08 14:45:38

郑州开辟高速与路政、交警“无缝对接”道路管控模式

​郑州开辟高速与路政、交警“无缝对接”道路管控模式,

2023-09-08 13:16:32

掘金全队2K24能力值:约基奇98 穆雷88 戈登84 小波特83

​直播吧9月8日讯 NBA2K24能力值正式公布,掘金全队能力值如下:约基奇

2023-09-08 11:47:18

刚刚,华为官宣:华为Mate60Pro+ 华为MateX5正式开启预订!有这几款配置,速看......

​刚刚,华为手机官宣:今天,华为Mate60Pro+、华为MateX5加入先锋计划,

2023-09-08 10:58:52

2023全国家居焕新消费季暨上海家居消费节正式启动

​  新华网上海9月8日电(记者王日晨)9月7日,在第52届中国家博会(上

2023-09-08 10:14:04

河南汉服爱好者演绎海外流失文物“归乡” 与河南博物院的“兄弟姐妹”跨时空重逢

​“虽然我们远在他乡,但请不要忘记我们,我想回家!”短短4天时间,顶

2023-09-08 09:25:54

这两个风口行业,你喜欢哪个?

​经历一波连续反弹后今天开始分化调整,上午指数和情绪同步走弱。对于指

2023-09-01 10:03:11

杭州地铁四期站点分布草案公示 萧山南部八镇首通地铁

​杭州地铁四期站点分布草案公示萧山南部八镇首通地铁杭州网发布时间:20

2023-09-01 09:00:19

全球最大乐高乐园主供电源工程开工

​深圳新闻网2023年9月1日讯(深圳特区报记者方胜通讯员赖伟文)昨日,11

2023-09-01 08:10:02

欧冠抽签:拜仁PK曼联,巴黎多特米兰纽卡死亡之组,曼城巴萨好签!

​今天晚上,新赛季欧冠小组赛抽签仪式进行,拜仁抽到了曼联,这是一组久

2023-09-01 06:44:16

基本情绪有哪些政治 基本情绪有哪些

​1、开心寂寞伤感嘻哈甜蜜励志浪漫怀旧抒情搞笑摇滚。相信通过基本情绪

2023-09-01 03:08:53

2023年8月31日线材价格最新行情预测

​中国报告大厅2023年8月31日线材价格最新走势监测显示:北京乾盛成钢铁

2023-08-31 22:29:12

鲲鹏此日乘风起 红小学子扬帆行——红山根小学2023年秋季开学典礼暨一年级新生入学仪式

​沐金风,杏坛披锦绣;唷清韵,学子谱新篇。在这满怀收获的初秋时节,红

2023-08-31 21:07:33

股价暴跌44%!市值一夜蒸发6050亿元!越南造车新势力被“打回原形”?

​越南电动汽车公司VinFast被“打回原形”。据彭博社报道,8月29日,越南

2023-08-31 19:24:16

四川省第十四届人民代表大会常务委员会任命名单

​四川人大四川省第十四届人民代表大会常务委员会任命名单2023年8月31日

2023-08-31 18:05:31

河南9名金融机构高管任职资格获批

​国家金融监督管理总局许昌监管分局核准:龚荣荣中国光大银行许昌分行副

2023-08-31 17:06:22

影坛不乏新人出头,独挑大梁尚需时日,香港新演员如今从哪来?

​不过,上述家喻户晓的港姐大多诞生于上世纪八九十年代,也就是香港娱乐

2023-08-31 15:55:09