为什么重复的GET请求变慢了?
最近在研究慢请求监控的问题,写了一个简单的测试代码:在网页端(index.html)通过fetch函数向服务端获取数据,然后打印请求耗时。
function requestData() {
let start = new Date();
fetch("http://localhost:3000/company/basic")
.then(res => {
return res.json();
})
.then(res => {
let span = new Date() - start;
console.log("span:", span);
});
}
requestData();
在服务端通过setTimeout延时1500s才返回数据(服务端使用ExpressJS)。
app.get("/company/basic", (req, res) => {
setTimeout(function() {
res.send({ hello: "Hello Fundebug!" });
}, 1500);
});
不出所料,span数据都略微大于 1500。
而后,我突发奇想,假设我同时发送多个请求会怎么样呢?于是有了如下代码:
[1, 2, 3].forEach(function() {
requestData();
});
结果好像也没问题,在 Chrome 浏览器下面是这个效果:

接入 Fundebug 慢请求监控测试
于是愉快地接入 Fundebug 监控:
<script
src="https://js.fundebug.cn/fundebug.1.9.0.min.js"
apikey="API-KEY"
></script>
并设置如果请求时长超过 2 秒就上报:
if ("fundebug" in window) {
fundebug.httpTimeout = 2000;
}
本以为刷新页面,应该不会收到报错。
结果,万万没想到的是,Fundebug 收到 2 个慢请求报错。

这不科学啊!
点开错误详情,可以看到具体的报错信息。一个请求耗时 3018 毫秒,一个请求耗时 4525 毫秒。

也就是说,第一个请求没问题,假设是 1500 毫秒。我们把三个请求的时间放一起看看有何规律:1500,3018,4524。他们近似成等差数列,相差 1500 毫秒。于是,我怀疑三个请求是一个一个阻塞式的,而不是并发的。
测试并发请求不同 API 的情况
为了验证这一点,我将测试改为请求三个不同的 API 接口。
服务端代码:
app.get("/company/basic", resp);
app.get("/company/basic1", resp);
app.get("/company/basic2", resp);
function resp(req, res) {
setTimeout(function() {
res.send({ hello: "Hello Fundebug!" });
}, 1500);
}
网页端代码(requestData函数传入请求的 URL):
[
"http://localhost:3000/company/basic",
"http://localhost:3000/company/basic1",
"http://localhost:3000/company/basic2"
].forEach(function(item) {
requestData(item);
});
为了获取请求数据,将httpTimeout改为 1500。
if ("fundebug" in window) {
fundebug.httpTimeout = 1500;
}
Fundebug 捕获三个请求的时间,分别为 1526,1525,1529。

至此大体验证了刚刚的假设:对同一个 API 接口的并发请求会被阻塞,对不同的 API 接口并发请求正常执行。
那么为什么会被阻塞呢?意图何在?接下来慢慢给各位介绍。
背后的原因
在StackOverflow上找到了答案:
Yes, this behavior is due to Chrome locking the cache and waiting to see the result of one request before requesting the same resource again. The answer is to find a way to make the requests unique.
也就是说,Chrome 特意做了这样的设计。对于连续的相同请求,Chrome 会阻塞后面的请求,直到前面的完成。通过判断前面的请求返回的 Header 里面的缓存设置来决定下一步的行动。
我们可以做个实验来验证一下。
缓存实验
服务端设置缓存 2 秒
在服务端的接口返回代码中配置缓存时间
res.setHeader("Cache-Control", "public, max-age=2");

服务端设置不缓存
res.setHeader(
"Cache-Control",
"private, no-cache, no-store, must-revalidate"
);

Chrome 开发者面板设置
Disable Cache
最后的疑问
为什么打开和不打开谷歌开发者控制台,行为会不一样了?
其实是有原因的,而且这个干扰项一度成功阻止了我发现问题的本质。当我们在开发前端项目的时候,代码的改动希望能够实时地反应到网页上,而不是受到浏览器缓存的影响,但是我们发现往往刷新页面的时候没有真的去服务端获取数据,还是老的信息。于是,我们会去配置一个选项,将Disable Cache设置为true。也就是说,在开发环境下,缓存是被禁用了的,也就不存在等待第一个请求返回然后判断其 Header 里面Cache-Control设置的问题。这也是为什么打开谷歌开发者控制台,请求没有等待,立即执行了。
关于Fundebug
Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!

版权声明
转载时请注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2019/07/17/chrome-stall-multiple-same-request/
为什么重复的GET请求变慢了?的更多相关文章
- Retrofit + RxJava + OkHttp 让网络请求变的简单-基础篇
https://www.jianshu.com/p/5bc866b9cbb9 最近因为手头上的工作做完了,比较闲,想着做一些优化.看到以前用的那一套网络框架添加一个请求比较麻烦,并且比较难用,所以想改 ...
- Request —— 让 Node.js http请求变得超简单
github地址: https://github.com/request/request 安装: npm install request --save-dev
- 【Nginx】解决Post请求变Get的问题
默认情况下Nginx会把post请求做一次重定向操作,然后后端收到的就成了Get请求,还会导致一些参数的遗漏. 日志如下: 172.16.1.108 - - [11/Jan/2019:18:27:09 ...
- aspnetcore webapi 解决发布以后每隔一段时间请求变缓慢
项目:netcore webapi 3.1 平台:windows server 2008 r2 服务器:IIS 7.5 项目发布到IIS以后第一次请求特别慢大概7.8秒,然后每隔5分钟请求一次大概2. ...
- 后勤数据源增量队列Delta Queue(RSA7)中的增量更新区Delta Update、增量重复区Delta Repetition
声明:原创作品,转载时请注明文章来自SAP师太技术博客:( 博/客/园www.cnblogs.com)www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- 代码的坏味道(14)——重复代码(Duplicate Code)
坏味道--重复代码(Duplicate Code) 重复代码堪称为代码坏味道之首.消除重复代码总是有利无害的. 特征 两个代码片段看上去几乎一样. 问题原因 重复代码通常发生在多个程序员同时在同一程序 ...
- MySQL中删除重复数据的简单方法,mysql删除重复数据
MYSQL里有五百万数据,但大多是重复的,真实的就180万,于是想怎样把这些重复的数据搞出来,在网上找了一圈,好多是用NOT IN这样的代码,这样效率很低,自己琢磨组合了一下,找到一个高效的处理方式, ...
- php-- 避免表单的重复提交
用户提交表单时可能因为网速的原因,或者网页被恶意刷新,致使同一条记录重复插入到数据库中,这是一个比较棘手的问题.我们可以从客户端和服务器端一起着手,设法避免同一表单的重复提交. 1.使用客户端脚本 提 ...
- js防止表单重复提交的两种方法
第一种:用flag标识,下面的代码设置checkSubmitFlg标志: 复制代码代码如下: <script language=""javascript"" ...
随机推荐
- BayaiM__MySQL错误对照表
BayaiM__MySQL错误对照表 原创 作者:bayaim 时间:2016-06-16 09:16:29 33 0删除编辑 ------------------------------------ ...
- Python—函数进阶篇
lambda表达式(匿名函数表达式) 作用:创建一个匿名函数对象.同def类似,但不提供函数名. 语法:lambda [形参1,形参2,...] : 表达式 语法说明 lambda 只是一个表达式,它 ...
- [Go] golang实时监控日志文件的包tail
在linux中有一个tail命令,tail -f可以实时的监控文件新增加的内容,如果用代码实现这个逻辑,可以下载使用这个包go get github.com/hpcloud/tail/... 测试代码 ...
- [MySQL] docker下安装使用mysql配置主从复制
拉取mysql的镜像docker search mysqldocker pull mysql 通过镜像创建容器,这里先创建第一个容器作为master mysql-v /etc/mysql:/etc/m ...
- 初学JavaScript正则表达式(二)
正则表达式的实例化与标识符 字面量: var reg = /\bis\b/g // \b--字符边界 g全文搜索 查找单词为is的字符 He is a boy. IS He? 构造函数: var re ...
- sudo apt-get 与 yum 常用命令
yum -RedHat:CentOS... -xxx.rpmsudo apt-get -Debian:Ubuntu... -xxx.deb 安装工具rpm -ivh yum-2.0.4-1.rh ...
- day66_10_10,vue项目环境搭建
一.下载. 首先去官网查看网址. 下载vue环境之前需要先下载node,使用应用商城npm下载,可以将其下载源改成cnpm: """ node ~~ python:nod ...
- MySQL 行格式
以 MySQL 默认的存储引擎 InnoDB 为例 InnoDB 包含以下四种行格式 Compact Redundant Dynamic Compressed 指定行格式 CREATE TABLE 表 ...
- 团队Git现场编程实战
团队Git现场编程实战 一.组员职责分工 组员 分工 贡献度 卢欢(组长) 前后端接口设计 8% 严喜 寻找相关资料 8% 张火标 设计并描述界面原型 8% 钟璐英 编写随笔 8% 周华 填写完善文档 ...
- [Algorithm] 1290. Convert Binary Number in a Linked List to Integer
Given head which is a reference node to a singly-linked list. The value of each node in the linked l ...