最近在研究慢請求監控的問題,寫了一個簡單的測試代碼:在網頁端(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/…