Stalled:一次請求超時異常


簡述

  最近項目中出現一個問題,前端每隔1秒向同一個url發起請求,第一次請求響應時間2秒左右,此后每次請求耗時會增加大約1秒,直到超時。

定位和驗證

  • 后台

    增加日志觀察后台服務耗時情況,發現每次均耗時2秒左右,和前端第一次請求耗時差不多,后續也沒有明顯增長,基本可以排除后台服務的問題

  • 前端

    首先是在項目指定的瀏覽器chrome上發現的問題,之后分別測試了edeg/firefox/ie,只在edeg上復現了問題,而edeg又是使用的Chromium內核,因此猜測是Chrome的某種機制導致的問題。

    打開chrome控制台,查看請求耗時的詳情,如下:

    

    觀察多次請求的耗時明細,發現真正發起請求到響應的時間依然穩定在2秒左右,這和后台觀察到的情況是一樣的。

    真正導致請求超時的是Connection Start:Stalled,這一項每次穩定增長1秒左右,最終導致超時。

    那么這個Stalled是何方神聖呢,chrome文檔(https://developer.chrome.com/docs/devtools/network/reference/)如下:

    Here's more information about each of the phases you may see in the Timing tab:

    •   Queueing. The browser queues requests when:
      •   There are higher priority requests.    有更高優先級的請求
      •   There are already six TCP connections open for this origin, which is the limit. Applies to HTTP/1.0 and HTTP/1.1 only.  針對每個源,最多打開6個TCP連接
      •   The browser is briefly allocating space in the disk cache    瀏覽器正在准備緩存
    •   Stalled. The request could be stalled for any of the reasons described in Queueing.

    Stalled:在滿足Queueing的任意一種條件時,請求將會停滯(阻塞)。

    更高優先級的請求此時並不存在,排除;TCP連接數量限制此時也未達上限,而且同網站的其他請求並未受影響,排除(對於TCP連接和數量限制后續可以再研究一下);

    同時在StackOverFlow上我找到一個類似的問題:https://stackoverflow.com/questions/27513994/chrome-stalls-when-making-multiple-requests-to-same-resource,高贊回答如下:

      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.

    I added a random number to the query string, and everything is working now.

    完美契合文檔中的緩存阻塞,馬上驗證

    

  驗證1:在請求url上添加隨機值

    將url由http://localhost:8080/master/timed修改為http://localhost:8080/master/timed?HxkNkz4Wwe

    結果:問題解決,所有請求並發進行,不再阻塞!    

 

     那么能不能通過后端的響應來解決問題呢?於是又做了一些驗證

  驗證2:修改響應頭,試圖控制瀏覽器的緩存行為

    分別修改Cache-Control響應頭的值為no-store/no-cache/no-store,no-cache/max-age=3, must-revalidate,觀察

    結果:前面3種毫無反應;對於第4種,結果如下:

    

    沒有緩存時,請求從后台獲取數據,然后緩存到本地;有緩存時,請求直接從磁盤緩存獲取數據(有時候還會獲取到空值),對於接口類的請求來說這有可能獲取到過期的數據,顯然是不可接受的,事實上,一般之后資源類的數據(js/css/圖片等)才會通過緩存獲取。

 

  另外,針對文檔中的第二點,TCP連接數量限制也做了一次驗證:

  驗證3:最大TCP連接數

  將前端請求頻率縮小為10ms一次並且帶上隨機值,並發數量超過6個以后的確會發生阻塞現象:      

 

 

 

  如圖,即使url不同也只能同時發出6個請求,前面的請求完成之后后面的才能進行。

 

 結論

在拿到響應之前,chrome會將資源(以url表示)相關的緩存鎖住,后續所有相同的url請求都必須在隊列中等待,直到前面的請求及緩存處理完之后才能依次進行。

解決這個問題有兩種方法:

1. 每次請求時在鏈接上加個隨機值

2. 對於完全相同的請求不並發進行,而是等上一個完成之后才進行下一個

Tips

這次解決這個問題花的時間稍微有點長,原因是一開始的方向有誤,直接去翻后台代碼和腳本浪費了不少時間。

正確的做法是首先確定問題發生在哪里:前端還是后端,如果是后端的話再看是服務器還是服務本身(老二分法了),確定是服務本身之后再去看細節,不花無謂的時間。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM