您好,登錄后才能下訂單哦!
最近在研究慢請(qǐng)求監(jiān)控的問(wèn)題,寫(xiě)了一個(gè)簡(jiǎn)單的測(cè)試代碼:在網(wǎng)頁(yè)端(index.html
)通過(guò)fetch
函數(shù)向服務(wù)端獲取數(shù)據(jù),然后打印請(qǐng)求耗時(shí)。
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();
在服務(wù)端通過(guò)setTimeout
延時(shí)1500s
才返回?cái)?shù)據(jù)(服務(wù)端使用ExpressJS)。
app.get("/company/basic", (req, res) => {
setTimeout(function() {
res.send({ hello: "Hello Fundebug!" });
}, 1500);
});
不出所料,span
數(shù)據(jù)都略微大于 1500。
而后,我突發(fā)奇想,假設(shè)我同時(shí)發(fā)送多個(gè)請(qǐng)求會(huì)怎么樣呢?于是有了如下代碼:
[1, 2, 3].forEach(function() {
requestData();
});
結(jié)果好像也沒(méi)問(wèn)題,在 Chrome 瀏覽器下面是這個(gè)效果:
于是愉快地接入 Fundebug 監(jiān)控:
<script
src="https://js.fundebug.cn/fundebug.1.9.0.min.js"
apikey="API-KEY"
></script>
并設(shè)置如果請(qǐng)求時(shí)長(zhǎng)超過(guò) 2 秒就上報(bào):
if ("fundebug" in window) {
fundebug.httpTimeout = 2000;
}
本以為刷新頁(yè)面,應(yīng)該不會(huì)收到報(bào)錯(cuò)。
結(jié)果,萬(wàn)萬(wàn)沒(méi)想到的是,F(xiàn)undebug 收到 2 個(gè)慢請(qǐng)求報(bào)錯(cuò)。
這不科學(xué)??!
點(diǎn)開(kāi)錯(cuò)誤詳情,可以看到具體的報(bào)錯(cuò)信息。一個(gè)請(qǐng)求耗時(shí) 3018 毫秒,一個(gè)請(qǐng)求耗時(shí) 4525 毫秒。
也就是說(shuō),第一個(gè)請(qǐng)求沒(méi)問(wèn)題,假設(shè)是 1500 毫秒。我們把三個(gè)請(qǐng)求的時(shí)間放一起看看有何規(guī)律:1500,3018,4524。他們近似成等差數(shù)列,相差 1500 毫秒。于是,我懷疑三個(gè)請(qǐng)求是一個(gè)一個(gè)阻塞式的,而不是并發(fā)的。
為了驗(yàn)證這一點(diǎn),我將測(cè)試改為請(qǐng)求三個(gè)不同的 API 接口。
服務(wù)端代碼:
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);
}
網(wǎng)頁(yè)端代碼(requestData
函數(shù)傳入請(qǐng)求的 URL):
[
"http://localhost:3000/company/basic",
"http://localhost:3000/company/basic1",
"http://localhost:3000/company/basic2"
].forEach(function(item) {
requestData(item);
});
為了獲取請(qǐng)求數(shù)據(jù),將httpTimeout
改為 1500。
if ("fundebug" in window) {
fundebug.httpTimeout = 1500;
}
Fundebug 捕獲三個(gè)請(qǐng)求的時(shí)間,分別為 1526,1525,1529。
至此大體驗(yàn)證了剛剛的假設(shè):對(duì)同一個(gè) API 接口的并發(fā)請(qǐng)求會(huì)被阻塞,對(duì)不同的 API 接口并發(fā)請(qǐng)求正常執(zhí)行。
那么為什么會(huì)被阻塞呢?意圖何在?接下來(lái)慢慢給各位介紹。
在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.
也就是說(shuō),Chrome 特意做了這樣的設(shè)計(jì)。對(duì)于連續(xù)的相同請(qǐng)求,Chrome 會(huì)阻塞后面的請(qǐng)求,直到前面的完成。通過(guò)判斷前面的請(qǐng)求返回的 Header 里面的緩存設(shè)置來(lái)決定下一步的行動(dòng)。
我們可以做個(gè)實(shí)驗(yàn)來(lái)驗(yàn)證一下。
服務(wù)端設(shè)置緩存 2 秒
在服務(wù)端的接口返回代碼中配置緩存時(shí)間
res.setHeader("Cache-Control", "public, max-age=2");
服務(wù)端設(shè)置不緩存
res.setHeader(
"Cache-Control",
"private, no-cache, no-store, must-revalidate"
);
Chrome 開(kāi)發(fā)者面板設(shè)置Disable Cache
為什么打開(kāi)和不打開(kāi)谷歌開(kāi)發(fā)者控制臺(tái),行為會(huì)不一樣了?
其實(shí)是有原因的,而且這個(gè)干擾項(xiàng)一度成功阻止了我發(fā)現(xiàn)問(wèn)題的本質(zhì)。當(dāng)我們?cè)陂_(kāi)發(fā)前端項(xiàng)目的時(shí)候,代碼的改動(dòng)希望能夠?qū)崟r(shí)地反應(yīng)到網(wǎng)頁(yè)上,而不是受到瀏覽器緩存的影響,但是我們發(fā)現(xiàn)往往刷新頁(yè)面的時(shí)候沒(méi)有真的去服務(wù)端獲取數(shù)據(jù),還是老的信息。于是,我們會(huì)去配置一個(gè)選項(xiàng),將Disable Cache
設(shè)置為true
。也就是說(shuō),在開(kāi)發(fā)環(huán)境下,緩存是被禁用了的,也就不存在等待第一個(gè)請(qǐng)求返回然后判斷其 Header 里面Cache-Control
設(shè)置的問(wèn)題。這也是為什么打開(kāi)谷歌開(kāi)發(fā)者控制臺(tái),請(qǐng)求沒(méi)有等待,立即執(zhí)行了。
Fundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應(yīng)用實(shí)時(shí)BUG監(jiān)控。 自從2016年雙十一正式上線,F(xiàn)undebug累計(jì)處理了10億+錯(cuò)誤事件,付費(fèi)客戶有陽(yáng)光保險(xiǎn)、核桃編程、荔枝FM、掌門(mén)1對(duì)1、微脈、青團(tuán)社等眾多品牌企業(yè)。歡迎大家免費(fèi)試用!
轉(zhuǎn)載時(shí)請(qǐng)注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2019/07/17/chrome-stall-multiple-same-request/
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。