最近在研究慢请求监控的问题,写了一个简单的测试代码:在网页端(index.html
)经过fetch
函数向服务端获取数据,而后打印请求耗时。html
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
延时1500ms
才返回数据(服务端使用ExpressJS)。前端
app.get("/company/basic", (req, res) => {
setTimeout(function() {
res.send({ hello: "Hello Fundebug!" });
}, 1500);
});
复制代码
不出所料,span
数据都略微大于 1500。chrome
然后,我突发奇想,假设我同时发送多个请求会怎么样呢?因而有了以下代码:express
[1, 2, 3].forEach(function() {
requestData();
});
复制代码
结果好像也没问题,在 Chrome 浏览器下面是这个效果:编程
因而愉快地接入 Fundebug 监控:json
<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 个慢请求报错。api
这不科学啊!浏览器
点开错误详情,能够看到具体的报错信息。一个请求耗时 3018 毫秒,一个请求耗时 4525 毫秒。
也就是说,第一个请求没问题,假设是 1500 毫秒。咱们把三个请求的时间放一块儿看看有何规律:1500,3018,4524。他们近似成等差数列,相差 1500 毫秒。因而,我怀疑三个请求是一个一个阻塞式的,而不是并发的。
为了验证这一点,我将测试改成请求三个不一样的 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专一于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对一、微脉、青团社等众多品牌企业。欢迎你们免费试用!
转载时请注明做者 Fundebug以及本文地址: blog.fundebug.com/2019/07/17/…