[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

IridescentMia發表於2018-08-21

效能退化的探索之旅

發現問題

這一切開始於一個警報,首頁應用的錯誤率高於 4% 的閾值。

顯示數千個錯誤頁面對我們的使用者產生了切實的影響(還好 CDN 快取抵消一部分影響)。

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

被使用者看到的錯誤頁面

應用程式的錯誤日誌顯示,該應用程式沒有任何有關 top stories 的資料。

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

診斷問題

首頁的工作原理是在一個時間間隔內輪詢 GraphQL api 以獲取資料,將該資料儲存在記憶體中並根據請求渲染它。從理論上講,如果請求失敗,應該保留之前穩定的資料。進一步深入日誌,我們看到對 GraphQL api 的請求失敗了,但是是有錯誤而不是超時——或者至少是不同型別的超時。

FetchError: response timeout at https://….&source=next-front-page over limit: 5000
複製程式碼

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

奇怪的是,API 的響應時間似乎遠低於首頁設定的 5 秒超時。這讓我們相信問題出現在首頁和應用程式之間的連線上。我們做了些嘗試——在兩者之間使用 keepAlive 連線,分散請求,這樣它們就不會同時發起。這些似乎都沒有產生任何影響。

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

更神祕的是 Heroku 上顯示的響應時間。第 95 百分位數約為 2-3 秒,而最大值有時達到 10-15 秒。由於首頁被 Fastly 高度快取,包括 stale-while-revalidate 頭,許多使用者可能不會注意到。但這很奇怪,因為首頁真的不應該做很多工作來渲染頁面。所有資料都儲存在記憶體中。

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

首頁的 Heroku 響應時間

因此我們決定對本地執行的應用程式副本進行一些分析。我們將通過使用 Apache Bench 每秒發出10個請求,共發出 1000 個請求來複制一些負載。

ab -n 1000 -c 10 http://local.ft.com:3002/
複製程式碼

使用 node-clinicnsolid,我們可以對記憶體、CPU 和應用程式程式碼有更深的理解。執行它們,確認我們可以在本地復現該問題。首頁需要 200-300 s 才能完成測試,超過 800 個請求不成功。相比之下,在文章頁面上執行相同的測試需要大約 50 秒。

測試用時:305.629 秒
完成的請求:1000
失敗的請求:876
複製程式碼

而且你看,n-solid 的圖表顯示事件迴圈的滯後超過 100 毫秒。

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

在做載入測試時事件迴圈滯後

使用 n-solid 的 CPU 分析器,我們可以精確定位阻塞事件迴圈的確切程式碼行。

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

火焰圖顯示導致滯後的函式

修復問題

罪魁禍首是...

return JSON.parse(JSON.stringify(this._data));
複製程式碼

對於每個請求,我們使用 JSON.parse/stringify 來建立資料的深克隆。這種方法本身不壞 —— 可能是深克隆比較快方法之一。但它們是同步方法,因此在執行時會阻塞事件迴圈。

在我們的案例中,這個方法在每個頁面渲染(對於每個被渲染的部分)中多次呼叫,具有大量資料(每次執行時整個頁面所需的資料),並且我們有幾個併發請求。由於 Javascript 是單執行緒的,因此這將對應用程式嘗試執行的所有其他操作產生連鎖反應。

深克隆資料的原因是,我們會根據請求中的一些資訊(例如,是否啟用了特定功能的切換),來改變物件。

為了解決這個問題——並減輕克隆所有內容的需要——我們在檢索時對物件應用了深度凍結,然後在資料被改動的地方克隆特定位。這仍然執行同步克隆——但僅限於更小的資料子集。

結論

修復好後,我們重新執行了負載測試,並在很短的時間內完成,0 次錯誤。

測試用時:37.476 秒
完成的請求:1000
失敗的請求:0
複製程式碼

我們釋出了修復程式,看到響應時間和錯誤(??)立即減少,並希望一些使用者開心!

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

修復後首頁的響應時間

[譯] 一行 JavaScript 程式碼竟然讓 FT.com 網站慢了十倍

關於未來

  • 對其他一些應用程式執行此分析,並瞭解我們可以進一步優化和/或減少 dyno 大小,這也會很有趣。
  • 我們可以讓事件迴圈更可見嗎?
  • 我們的首頁應該是 stale-on-error——所以為什麼仍然會看到數以千計的錯誤頁面?這個數字好還是壞?

感謝 Samuel Parkinson 的付出。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章