资讯专栏INFORMATION COLUMN

为什么在页面上操作几次之后就变得奇慢无比,接口长时间处于pending状态?

Rainie / 1538人阅读

摘要:开发环境前端后台浏览器部署系统问题现象在现有项目的基础之上增加了两个页面,但是在使用的过程中发现,当连续操作几次之后页面会变得奇慢无比,查看接口调用发现接口请求长时间处于状态,但是等分钟左右接口还是会返回应答结果。

开发环境
前端:Vue 2.0
后台:Node Express
浏览器:Chrome
部署系统:Linux
问题现象

在现有项目的基础之上增加了两个页面,但是在使用的过程中发现,当连续操作几次之后页面会变得奇慢无比,查看接口调用发现接口请求长时间处于pending状态,但是等1-2分钟左右接口还是会返回应答结果。如下图所示:

原因分析

通过反复复现该问题(在各个页面之间不同切换,触发请求),发现了一个规律,就是每次在第7次页面切换的时候,所有接口都会被阻塞并在1分多钟之后才返回。

看看这1分多钟究竟花在了哪里?

从上图可以看到,整个接口请求的大部分时间都花在了Stalled阶段。现在的问题是Stalled是啥意思?下面是一段比较浅显的解释:

Time the request spent waiting before it could be sent. This time is inclusive of any time spent in proxy negotiation.Additionally, this time will include when the browser is waiting for an already established connection to become available for re-use, obeying Chrome’s maximum six TCP connection per origin rule.

从上面的解释看,可能有两个原因:

TCP连接出问题了,一直无法建链成功;

TCP连接是OK的,但是一直被占用无法使用。

首先看第一个问题,TCP连接是否正常?

$ lsof -i:8700
COMMAND   PID     USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
node    26315 mumingv   12u  IPv4 449215145     0t64  TCP *:8700 (LISTEN)
node    26315 mumingv   16u  IPv4 449217569     0t64  TCP localhost:8700->172.24.186.14:54064 (ESTABLISHED)
node    26315 mumingv   17u  IPv4 449217570     0t64  TCP localhost:8700->172.24.186.14:54065 (ESTABLISHED)
node    26315 mumingv   18u  IPv4 449217580     0t64  TCP localhost:8700->172.24.186.14:54066 (ESTABLISHED)
node    26315 mumingv   19u  IPv4 449217581     0t64  TCP localhost:8700->172.24.186.14:54067 (ESTABLISHED)
node    26315 mumingv   20u  IPv4 449226874     0t64  TCP localhost:8700->172.24.186.14:54574 (ESTABLISHED)
node    26315 mumingv   21u  IPv4 449217583     0t64  TCP localhost:8700->172.24.186.14:54069 (ESTABLISHED)

上图中,8700是网站的服务端口号,172.24.184.14是Chrome浏览器所在Mac的IP。在复现问题的过程中一直执行lsof -i:8700持续进行观察发现,当在第7次页面切换的时候,这里的TCP连接数量不再增加,维持在6个左右且状态都是ESTABLISHED(已建立)。所以可以基本排除TCP连接的问题。

当然,也可以通过netstat命令查询TCP连接状态。

$ netstat -tunpa | grep 8700
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:8700                0.0.0.0:*                   LISTEN      27765/node          
tcp        0      0 10.95.199.140:8700          172.24.186.14:65413         ESTABLISHED 27765/node          
tcp        0      0 10.95.199.140:8700          172.24.186.14:65412         ESTABLISHED 27765/node          
tcp        0      0 10.95.199.140:8700          172.24.186.14:65421         ESTABLISHED 27765/node          
tcp        0      0 10.95.199.140:8700          172.24.186.14:65420         ESTABLISHED 27765/node          
tcp        0      0 10.95.199.140:8700          172.24.186.14:65419         ESTABLISHED 27765/node          
tcp        0      0 10.95.199.140:8700          172.24.186.14:65422         ESTABLISHED 27765/node          

再来看第二个问题,TCP连接被谁占用了不释放?

看看是不是有其他请求占用了这些TCP连接,查看所有请求,果不其然:

原来每次在页面切换的时候,浏览器都会默认发送一个请求获取一次网页图标,这个不是前端业务逻辑主动调用的XHR请求,但对于后端来说也是一次GET请求。

实际上,如果没有要求显示特定网页图标的话,后端随便返回一个信息就好了,不用非得准备一个网页图标。浏览器拿不到图标的话会显示一个默认图标。

问题找到了,看看为啥后端为啥没有返回图标并加以解决就好了。具体到这个项目,是在node express的app.js入口文件中没有注册相应的处理逻辑。

// 接口路由
loadRouter(app, "/project-name", path.join(__dirname, "app/controllers"));

// 静态页面
app.use("/project-name", express.static(path.join(__dirname, "webroot", "project-name")));

// favicon.ico和其他不支持的请求
app.get("*", function(req, res) {
    if (req.path === "/favicon.ico") {
        return;  // !!!这里不能直接return,需要返回具体的内容,否则会阻塞express框架返回应答消息!!!
    }   
    throw new PathError();
});

知道问题后,修改就很简单了。

app.get("*", function(req, res) {
    if (req.path === "/favicon.ico") {
        res.json({"status":0, msg:""});  // 这里随便返回个内容就行,不影响浏览器使用默认图标进行展示
    }   
    throw new PathError();
});

至此,问题解决。

FAQ Q:为什么浏览器和服务端之间最多只能创建6个TCP连接?

TCP连接资源数量有限,如果不限制数量的话,所有TCP全部被占用的话系统就“无法提供服务”了。一般浏览器的并发TCP连接数量都在5、6个左右,对于Chrome来说是6个。至于为什么是这么多,这是各浏览器自行设置的,没有标准。具体解释参考:官方文档。

Q:后续如何排查这类接口问题?

一般按照如下几步进行排查即可:

浏览器端看XHR请求,判断XHR请求本身是否有异常;

浏览器端看ALL请求,判断非XHR请求是否有异常;

服务器端查看服务本身是否正常;

服务器端查看服务建立的TCP连接是否正常;

抓包查看TCP交互和业务请求交互报文是否有异常。

参考资料

Network Issues Guide

Understanding Resource Timing

chrome的timeline中stalled问题解析

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109935.html

相关文章

  • mysql锁机制浅析(1)

    摘要:当多个客户端并发访问同一个数据的时候,为了保证数据的一致性,数据库管理系统会自动的为该数据加锁解锁,这种被称为隐式锁。 mysql最初是希望设计出一种独立于各种存储引擎的锁定机制,mysql存储引擎的设计者是建立在任何表在同一时刻只允许单个线程对其进行访问(包括读)这样的假设的基础之上的!很明显,现在的mysql并不是这个样子的,因为mysql如今已经发展成为了一款多用户、多线程的my...

    jiekechoo 评论0 收藏0
  • VIM问题合集(持续更新)

    摘要:在模式下粘贴速度很慢的问题一般当我们在模式下粘贴一段超大量的文本,比如行。更新后无法打开问题很久不使用安装东西,安装了一个小软件,结果直接更新到版本,然后导致完全无法打开。 Vim 在Insert模式下粘贴速度很慢的问题 一般当我们在Insert模式下粘贴一段超大量的文本,比如1000行。那么Vim会变得奇慢无比,大概半分钟? 所以,如果我们要粘贴文本,需要用另一种方法:在Normal...

    RyanHoo 评论0 收藏0
  • 如何开发高效的 Android 应用

    摘要:耗电或者内存占用等影响产品效率的每一个问题都会影响的成功。即使高效的代码也是需要时间来运行。最高效的方式就是在类这一级完成这项操作,可以使用或者来创建后台操作。 假如要Google Play上做一个最失败的案例,那最好的秘诀就是界面奇慢无比、耗电、耗内存。接下来就会得到用户的消极评论,最后名声也就臭了。即使你的应用设计精良、创意无限也没用。 耗电或者内存占用等影响产品效率的每一个问题...

    Jinkey 评论0 收藏0
  • 【腾讯Bugly干货分享】基于RxJava的一种MVP实现

    摘要:每周都会举行嘉宾分享,话题讨论等活动。本期,我们邀请了腾讯开发工程师戴俊,为大家分享基于的一种实现。分享内容简介是一个实现响应式编程的库,让异步事件以序列的形式组织。目前在腾讯动漫主要负责端的开发工作。接口的第二个作用是可以用来切换实现。 本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57bfe... Dev Cl...

    DevTalking 评论0 收藏0
  • 解决局域网连接MySql奇慢无比的问题

    [mysqld] skip-name-resolve

    oysun 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<