阿里面试:页面调10 个上游接口,如何做高并发?

news2024/11/29 1:28:34

说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易的面试资格,遇到很多很重要的面试题:

一个页面要调100 个上游接口,如何优化?

一个场景题,一个页面中,需要通过调用下游系统的很多很多个接口来获取数据,请问最优的高并发设计方案?

就在昨天, 一个小伙伴面试阿里, 遇到这道题:

场景题:一次要调10 个上游接口,如何做高并发?

小伙伴 没有回答好,导致面试挂了,来求助尼恩,如何才能回答得很漂亮, 让面试官刮目相看、口水直流。

尼恩提示:并发调用的知识,既是面试的核心知识,又是开发的核心知识。

所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V114版本PDF集群,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,后台回复:领电子书

文章目录

    • 说在前面
    • 正确、最佳的答题姿势
    • 全链路如何拆分后,进行优化和改造呢?
    • 多层次如何拆分后,进行优化和改造呢?
    • 多维度优化和改造,主要有哪些呢?
    • 业务线程,IO线程,如何进行优化和改造呢?
    • 网络传输维度,如何进行优化和改造呢?
    • 方案一实操
      • 方案一性能基线测试
    • 方案二实操:
      • 方案二性能基线测试
    • 方案三实操:
      • 方案三性能基线测试
    • 说在最后
    • 推荐阅读

正确、最佳的答题姿势

尼恩给大家梳理一下正确、最佳的答题姿势。有了这个姿势,面试官自然爱到 “不能自已、口水直流”

页面调10 个上游接口,如何做高并发?

首先, 告诉面试官, 解决这个问题,需要 全链路、多层次、多维度进行 优化和改造

全链路如何拆分后,进行优化和改造呢?

庖丁解牛,但是也不需要太复杂,可以把简单的把全链路拆分如下:

  • 接受下游请求环节
  • 上游请求分裂后入列环节
  • 执行上游调用环节
  • 上游结果聚合和响应环节

多层次如何拆分后,进行优化和改造呢?

核心的措施是:异步化, 但是,要分层进行异步化

可以简单的, 把用户的api调用解耦为三层, 如下图所示:

  • 应用层:编程模型的异步化
  • 框架层:IO线程的异步化
  • OS层: IO模型的异步化

解耦之后,再庖丁解牛,一层一层的进行异步化架构。

如何实现全链路异步,知识非常多,具体请参见尼恩的《全链路异步,让你的 SpringCloud 性能优化10倍+》、《NIO学习圣经》等深度文章。

建议大家把以上这些文章,作为作为一个体系来学习。

多维度优化和改造,主要有哪些呢?

其实有很多, 但是主要聚焦的点是:

业务线程,IO线程,如何进行优化和改造呢?

前面已经庖丁解牛,但是也不需要太复杂,可以把简单的把全链路拆分如下:

  • 接受下游请求环节
  • 上游请求分裂后入列环节
  • 执行上游调用环节
  • 上游结果聚合和响应环节

第二个环节涉及到了 业务线程池,第三个环节设计到了 IO线程池

都需要合理的设置线程池的大小、拒绝策略。 并且结合不同的框架和组件,结合自己的业务场景实现异步。

比如:

方案一: 使用 CompletableFuture (自定义业务线程池) + httpClient (池化同步io框架) + CountDownLatch闭锁(聚合结果)

方案二: CloseableHttpAsyncClient(池化异步io框架) + CountDownLatch闭锁(聚合结果)

方案三: 自研Netty 异步IO框架+ CountDownLatch闭锁(聚合结果)

网络传输维度,如何进行优化和改造呢?

核心就是两点:

  • 连接复用
  • 多路复用

首先看 连接复用。 也就是 短链接,变成长连接

http1.0协议头里可以设置Connection:Keep-Alive。在header里设置Keep-Alive可以在一定时间内复用连接,具体复用时间的长短可以由服务器控制,一般在15s左右。

到http1.1之后Connection的默认值就是Keep-Alive,如果要关闭连接复用需要显式的设置Connection:Close。

多路复用代替原来的序列和阻塞机制。所有就是请求的都是通过一个 TCP 连接并发完成。

因为在多路复用之前所有的传输是基于基础文本的,在多路复用中是基于二进制数据帧的传输、消息、流,所以可以做到乱序的传输。

多路复用对同一域名下所有请求都是基于流,所以不存在同域并行的阻塞。

多路复用复用场景,多次请求如下图:

http2.0

尼恩提示:由于http1.2 目前普及度不够,一般还是考虑 http1.1连接复用

方案一实操

使用 CompletableFuture (自定义业务线程池) + httpClient (池化同步io框架) + CountDownLatch闭锁(聚合结果)

static final HttpClient httpClient = HttpClientBuilder.create().build();

@Benchmark
@Test
public void HttpClient() {
    List<String> apis = Arrays.asList(
        "http://192.168.56.121/echo?i=1",
        "http://192.168.56.121/echo?i=2",
        "http://192.168.56.121/echo?i=3"
    );

    CountDownLatch latch = new CountDownLatch(apis.size());
    Map<String, String> results = new ConcurrentHashMap<>();
    for (String api : apis) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            HttpResponse response = null;
            try {
                response = httpClient.execute(new HttpGet(api));
            } catch (IOException e) {
                return null;
            }

            if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
                try {
                    return EntityUtils.toString(response.getEntity());
                } catch (Exception e) {
                    log.error("error", e);
                    throw new RuntimeException(e);
                }
            }
            return null;
        });
        future.whenComplete((result, throwable) -> {
            latch.countDown();
            results.put(api, result);
        }
                           );
    }

    try {
        latch.await(10000000, TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        log.error("error", e);

        //            throw new RuntimeException(e);
    }

    //        System.out.println(results.toString());
}

方案一性能基线测试

这里使用linux + jmh 框架,完成性能基线测试

达到 5000qps

方案二实操:

方案二:CloseableHttpAsyncClient(池化异步io框架) + CountDownLatch闭锁(聚合结果)

这里去掉了业务线程池。 提升了性能

@Benchmark
@Test
public void HttpAsyncClient() {

    List<String> urls = Arrays.asList(
        "http://192.168.56.121/echo?i=1",
        "http://192.168.56.121/echo?i=2",
        "http://192.168.56.121/echo?i=3"
    );

    CountDownLatch latch = new CountDownLatch(urls.size());
    Map<String, String> results = new ConcurrentHashMap<>();

    for (int i = 0; i < urls.size(); i++) {
        final String url = urls.get(i);
        Future<HttpResponse> f0 = asyncClient.execute(new HttpGet(url), new FutureCallback<HttpResponse>() {
            public void completed(HttpResponse response) {

                latch.countDown();

                if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
                    try {
                        String result = EntityUtils.toString(response.getEntity());
                        results.put(url, result);
                    } catch (IOException e) {
                        log.error("error", e);
                        //                            throw new RuntimeException(e);
                    }
                }

            }

            public void failed(Exception ex) {
                latch.countDown();
                log.error("error", ex);
                //                    ex.printStackTrace();
            }

            public void cancelled() {
                latch.countDown();
            }
        });
    }


    try {
        latch.await(10000000, TimeUnit.MILLISECONDS);
    } catch (
        InterruptedException e) {
        throw new RuntimeException(e);
    }

    //        System.out.println("results = " + results);
}

方案二性能基线测试

这里使用linux + jmh 框架,完成性能基线测试

达到 4300qps

理论上性能会更优,但是,看上去性能更差。

why?

具体原因,尼恩还在思考, 有写眉目, 具体可以来技术自由圈社群交流。

方案三实操:

方案三: 自研Netty 异步IO框架+ CountDownLatch闭锁(聚合结果)

尼恩在网上找了一个 生产环境上的 qps达到 9000的 自研Netty 异步IO框架

完成了实验

@Benchmark
@Test
public void nettyGet() {

    //        setup();
    List<String> urls = Arrays.asList(
        "/echo?i=1",
        "/echo?i=2",
        "/echo?i=3"
    );

    CountDownLatch latch = new CountDownLatch(urls.size());
    Map<Integer, Object> results = new ConcurrentHashMap<>();


    ReqOptions options = new ReqOptions(TypeReference.from(String.class));
    for (int i = 0; i < urls.size(); i++) {

        int finalI = i;
        longConnHttpClient.getAsync("/echo?i=" + i, options, response -> {

            Object content = response.content();
            results.put(finalI, content);

            latch.countDown();
        }, e -> {
            e.printStackTrace();
            latch.countDown();
        });

    }

    try {
        latch.await(10000000, TimeUnit.MILLISECONDS);
    } catch (
        InterruptedException e) {
        throw new RuntimeException(e);
    }
    //        System.out.println("results = " + results);

}

方案三性能基线测试

这里使用linux + jmh 框架,完成性能基线测试

第一次个版本,性能就 300 ops

尼恩都晕了

进行优化之后,第二次验证, 也就4300qps, 但是,看上去性能也很差。

不用jmh进行基线测试, 达到 6000多qps,这下明白了。

为啥 技术越高明,性能反而不高呢?

还有有原因的,当然目前来看,具体的原因尼恩也是猜测,不能大放厥词。

具体的原因,尼恩还需要再做实验验证一下,确认之后再发布。

但是,目前没时间了,在录制《10Wqps netty网关架构与实操》, 所以,这里做个遗留问题,后面录个视频说吧。

说在最后

异步调用面试题,是非常常见的面试题。

以上的内容,如果大家能对答如流,如数家珍,基本上 面试官会被你 震惊到、吸引到。

在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,并且在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。

最终,让面试官爱到 “不能自已、口水直流”。offer, 也就来了。

推荐阅读

《百亿级访问量,如何做缓存架构设计》

《多级缓存 架构设计》

《消息推送 架构设计》

《阿里2面:你们部署多少节点?1000W并发,当如何部署?》

《美团2面:5个9高可用99.999%,如何实现?》

《网易一面:单节点2000Wtps,Kafka怎么做的?》

《字节一面:事务补偿和事务重试,关系是什么?》

《网易一面:25Wqps高吞吐写Mysql,100W数据4秒写完,如何实现?》

《亿级短视频,如何架构?》

《炸裂,靠“吹牛”过京东一面,月薪40K》

《太猛了,靠“吹牛”过顺丰一面,月薪30K》

《炸裂了…京东一面索命40问,过了就50W+》

《问麻了…阿里一面索命27问,过了就60W+》

《百度狂问3小时,大厂offer到手,小伙真狠!》

《饿了么太狠:面个高级Java,抖这多硬活、狠活》

《字节狂问一小时,小伙offer到手,太狠了!》

《收个滴滴Offer:从小伙三面经历,看看需要学点啥?》

《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1069720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka是什么,以及如何使用SpringBoot对接Kafka

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 系列文章目录一、Kafka与流处理二、Spring Boot与Kafka的整合Demo1. 新建springboot工程2. 添加Kafka依赖3. 配置Kafka4. 创建Kafka…

TCP/IP网络协议通信函数接口

创建套接字函数 socket 【头文件】 #include <sys/types.h> #include <sys/socket.h> 【函数原型】 int socket(int domain, int type, int protocol); 【函数功能】 socket 函数创建一个通信端点&#xff0c;并返回一个引用该端点的文件描述符&#xff0c;…

C++入门-day03

引言&#xff1a;本节我们讲一下C中的引用、内联函数、Auto、范围for 一、引用 先看一下下面这段代码&#xff1a; 在这段代码中。我们命名了两个变量&#xff0c;a和_a&#xff0c;其中_a就是a的引用 所谓引用就是a的“别名”&#xff0c;我们看一下这段代码的运行结果&…

互联网云厂商大转向:在海外重燃新「战事」

2023&#xff0c;云厂出海的第七个年头&#xff0c;三朵云的海外布局都在加速&#xff0c;在“主动出海”的大背景下&#xff0c;云厂的海外战场也正在发生新的变化。 作者|思杭 编辑|皮爷 出品|产业家 中国云厂&#xff0c;正在将目光从东南亚转移至中东。 东南亚的互…

代码随想录算法训练营第四十六天 | 518. 零钱兑换 II、377. 组合总和 Ⅳ

518. 零钱兑换 II 视频讲解&#xff1a;动态规划之完全背包&#xff0c;装满背包有多少种方法&#xff1f;组合与排列有讲究&#xff01;| LeetCode&#xff1a;518.零钱兑换II_哔哩哔哩_bilibili 代码随想录 &#xff08;1&#xff09;代码 377. 组合总和 Ⅳ 视频讲解&…

【哈士奇赠书活动 - 41期】- 〖产品设计软技能:创业公司篇〗

文章目录 ⭐️ 赠书 - 《产品设计软技能&#xff1a;创业公司篇》⭐️ 内容简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 赠书活动 → 获奖名单 ⭐️ 赠书 - 《产品设计软技能&#xff1a;创业公司篇》 ⭐️ 内容简介 在创业公司设计产品与在成熟公司设计产品存在明显差异。《产品设计软…

华为防火墙项目

二、知识点 1&#xff0c;会话表&#xff1a;防火墙通过首包建立会话表&#xff0c;其他非首包通过匹配会话表进行通信&#xff0c;就不用查看安全策略啦。 2&#xff0c;长连接 防火墙为各种协议设定了会话老化机制。当一条会话在老化时间内没有被任何报文匹配&#xff0c;则…

【算法|动态规划No.15】leetcode1035. 不相交的线

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

【教程】在RK3568上部署(C++)语义分割算法BiSeNetv1/v2

引言 在本篇教程中&#xff0c;博主将记录国庆假期前在RK3568上部署分割算法的步骤以及代码。首先说一下&#xff0c;RK3568这个开发板本身的算力大概是0.8T&#xff08;在实际开发中还会用到额外的计算卡&#xff0c;额外的计算卡后面文章再说&#xff0c;本篇文章主要记录在…

AQS的简单说明

1.概述 AQS全称AbstractQueuedSynchronizer&#xff0c;是用来实现锁或者队列同步器的公共基础部分的抽象实现&#xff0c;是整个JUC体系的基石&#xff0c;用于解决锁分配给谁的问题&#xff0c;ReentrantLock底层的实现就是AQS。 2.AQS实现原理 AQS内部有一个由volatile修…

正点原子嵌入式linux驱动开发——Linux内核顶层Makefile详解

之前的几篇学习笔记重点讲解了如何移植uboot到STM32MP157开发板上&#xff0c;从本章就开始学习如何移植Linux内核。 同uboot一样&#xff0c;在具体移植之前&#xff0c;先来学习一下Linux内核的顶层Makefile文件&#xff0c;因为顶层 Makefile控制着Linux内核的编译流程。 L…

如何在Apache和Resin环境中实现HTTP到HTTPS的自动跳转:一次全面的探讨与实践

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

提取歌曲伴奏?用对软件一键帮你搞定~

相信大家经常想获取某首歌曲的伴奏&#xff0c;但是不知从何下手&#xff0c;今天这篇教程给大家分享一个超神奇软件&#xff0c;一键提取歌曲伴奏&#xff01; 第一步&#xff1a;打开【音分轨】APP&#xff0c;进入首页点击【人声分离】 第二步&#xff1a;选择导入方式&…

多电脑之间无线访问文件夹传输文件之“电子神偷”

目录 应用场景说明网络共享文件功能开启步骤1&#xff1a;确保电脑开启网络共享功能步骤2&#xff1a;在自己电脑某个盘创建一个文件夹&#xff0c;作为共享文件夹步骤3&#xff1a;查看当前电脑的用户名和ip地址 访问网络共享文件夹&#xff0c;在电脑B访问获取电脑A的文件数据…

Windows版MySql8.0安装(亲测成功!)

下载 下载地址&#xff1a;点我下载 下载完成后将其解压到自定义目录下,我所有的软件都保存在C:\zhushanglin\WindowsSoft&#xff0c;解压完成后会看见以下目录: 配置环境变量 此电脑 右键,然后点属性&#xff0c;步骤如下: 新建MYSQL_HOME系统变量 编辑Path系统变量&a…

读论文:Real-Time Encrypted Traffic Classification via Lightweight Neural Networks

基于轻量级神经网络的实时加密流量分类 0、摘要 提出一种轻量级模型&#xff0c;设计原则“maximize the reuse of thin modules”&#xff0c;thin modules采用多头注意和一维卷积网络。由于所有数据包的一步交互和多头注意力机制的并行计算&#xff0c;所提出的模型的优势是…

RF元素定位

元素定位方式&#xff1a;id, name, link, partial_link_text, xpath, css id 【登录输入框】id session_email_or_mobile_number input text id session_email_or_mobile_numbername 【登录输入框】name session[email_or_mobile_number] input text name sessi…

react-antd 文件导入按钮增加一个加载状态

1、效果图实例: 2、部分代码 2.1 props : 2.2 handleChange、上传的文件检验 : construction中定义 construction(props) { super(props); this.state { loadingStaus: flase, loadingDisabled: flase, // 作用:按钮如果在加 载状态中&#xff0c;没…

Android多线程学习:线程

一、概念 进程&#xff1a;系统资源分配的基本单位&#xff0c;进程之间相互独立&#xff0c;不能直接访问其他进程的地址空间。 线程&#xff1a;CPU调度的基本单位&#xff0c;线程之间共享所在进程的资源&#xff0c;包括共享内存&#xff0c;公有数据&#xff0c;全局变量…

【Pod】

Pod 一、Pod基本概念二、Pod的使用方式pause容器&#xff08;pod的基础容器&#xff09;核心功能pause容器使得Pod中所有容器可以共享两种资源&#xff1a;网络和存储网络存储 三、Pod分类自主式Pod/静态pod控制器管理的Pod 四、三种容器五、镜像拉取策略&#xff08;image Pul…