為什麼重複的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-multi