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

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

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接口实现业务处理的项目,在使用过程中要关注调用链路上的资源复用,尤其长链路的调用,要深入考虑内存资源的利用以及对底层服务的压力。

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

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

作者:京东零售 王江波

来源:京东云开发者社区

标签:

(责任编辑:)

相关文章

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

​正值618大促,各方接口的调用都会大幅度增加。通过梳理接口依赖关系来

2023-06-27 10:26:55

谈情说爱分集剧情介绍_谈情说爱大结局

​1、我今天正好看,因为我也喜欢看,结局是那个犀利妹(徐小丽)终于原

2023-06-27 09:56:40

qq炫舞手游宠物蛋在哪里买_qq炫舞手游宠物|每日热点

​1、玩家可以在商店中购买宠物蛋,宠物蛋可以作为系统任务和礼物,获得

2023-06-27 10:00:58

【全球快播报】警号38008 谢谢您!

​警号38008谢谢您!

2023-06-27 09:20:21

环球时讯:三一重工:6月26日融资净买入1311.54万元,连续3日累计净买入1549.36万元

​6月26日,三一重工(600031)融资买入3969 78万元,融资偿还2658 24万

2023-06-27 08:51:53

天天新动态:九联科技(688609)6月26日主力资金净卖出3856.59万元

​截至2023年6月26日收盘,九联科技(688609)报收于10 41元,下跌7 05%,

2023-06-27 08:39:54

世界滚动:骂醒我歌词图片_骂醒我歌词

​1、有的时候很想玩[00:27 10]有的时候只想和你作伴[00:30 67]心情不停

2023-06-27 07:55:51

快看:如何培养新时代的“大先生”

​6月19日,在音乐思政课上,沈阳药科大学的700余名师生观看动人的电影片

2023-06-27 06:53:27

赞!黄埔街道乌涌口袋公园添新彩!|全球时快讯

​第36个“6·26”国际禁毒日之际,为进一步扩展禁毒宣传教育阵地覆盖面

2023-06-27 06:15:49

天天消息!【即将结束】口袋卡2限时优惠(附免费拿攻略)

​体验设计口袋系列共三套:1+2+用研,现已完结。买过Ⅰ的老用户买Ⅱ立减

2023-06-27 05:17:45

范燕云_关于范燕云的简介-头条焦点

​音频解说1、范燕云,电视剧《人生若如初相见》中人物,由郑罗茜饰演。

2023-06-27 04:44:01

6月26日基金净值:万家精选混合A最新净值1.3362,跌1.47%

​6月26日,万家精选混合A最新单位净值为1 3362元,累计净值为2 6285元,

2023-06-27 02:34:30

镇江卫校助产专业(镇江卫校)

​1、卫校是还好,但不要镇江卫校,花4年的时候学个中专。2、还不如花5年

2023-06-27 00:39:42

深圳港集团与汕尾市政府签署战略合作框架协议_环球观焦点

​据深圳港集团消息,6月26日深圳港集团与汕尾市政府签署战略合作框架协

2023-06-26 22:30:49

闲鱼用户超五亿,爱回收连续三个季度盈利,二手电商讲出新故事?

​以闲鱼、转转、爱回收为代表的二手电商发展迅速,迎来新的变化。据悉,

2023-06-26 21:49:11

股东权利的内容包括什么|天天观察

​股东权利的内容包括以下事项:1、股东身份权;2、参与决策权;3、选择

2023-06-26 21:08:53

2023年“6·26”国际禁毒日琼海市禁毒三年“固本防风险”行动启动_环球头条

​今年是禁毒三年“固本防风险”行动开局之年,为进一步增强广大人民群众

2023-06-26 20:17:18

健康知识宣传资料_健康知识 全球热推荐

​欢迎观看本篇文章,小升来为大家解答以上问题。健康知识宣传资料,健康

2023-06-26 19:46:56

世界热消息:光威复材:公司风电碳梁业务目前以境外客户为主 境内业务尚未没有形成批产

​每经AI快讯,有投资者在投资者互动平台提问:近期中材科技和金风科技等

2023-06-26 19:08:54

核的拼音怎么写_核的拼音 天天观速讯

​1、核拼音:[hé,hú]核[释义][hé]:1 果实中坚硬并包含果仁的部分:

2023-06-26 18:39:53

金博股份:将获2023年产业扶持资金1亿元

​金博股份晚间公告,公司近日收到益阳高新区财政局益阳高新区产业发展局

2023-06-26 18:22:31

华阳集团:目前公司已为国内外众多车企配套HUD产品 世界报道

​每经AI快讯,有投资者在投资者互动平台提问:你家的hud是不是很牛,自

2023-06-26 18:00:52

桧怎么读_桧具体怎么读|最新资讯

​1、怎么念:拼音:[古]、[胡]。2、“图形用户界面”的基本定义:3、(1)

2023-06-26 17:38:55

腾讯云首次完整披露自研星脉高性能计算网络|全球最资讯

​电商报快讯:6月26日消息,腾讯云首次对外完整披露自研星脉高性能计算

2023-06-26 17:18:15

京东物流与南航物流二期合作开启,打造一体化智能航空物流标杆 环球热讯

​京东物流与南航物流二期合作开启,打造一体化智能航空物流标杆:据京东

2023-06-26 16:48:11

天天日报丨“世纪大和解”!“黑粉”梁家辉代言长安马自达CX-50行也!

​(原标题:“世纪大和解”!“黑粉”梁家辉代言长安马自达CX-50行也!

2023-06-26 16:28:40

存货大增计提却下降 160亿龙头调节利润? 世界新动态

​【存货大增计提却下降160亿龙头调节利润?】6月25日深夜,UPS龙头易事

2023-06-26 16:08:58

世界互联网大会数字文明尼山对话今日在山东曲阜召开

​世界互联网大会数字文明尼山对话今天(26日)在山东济宁曲阜召开,主题

2023-06-26 15:38:30

环球即时看!抢登大战!费城强势出击,两方面印证哈登加盟湖人,恩比德或离开

​抢登大战!费城强势出击,两方面印证哈登加盟湖人,恩比德或离开,湖人,

2023-06-26 15:05:01

美国一男子加油站纵火:前脚与员工一言不合,后脚点燃垃圾桶 全球观热点

​美国一男子一言不合就放火(美国《纽约邮报》视频片段)据美国《纽约邮

2023-06-26 15:07:01