开发环境
前端: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交互和业务请求交互报文是否有异常。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。