- 原文地址:This one line of Javascript made FT.com 10 times slower
- 原文做者:Arjun
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:IridescentMia
- 校對者:Eternaldeath, Park-ma
這一切開始於一個警報,首頁應用的錯誤率高於 4% 的閾值。javascript
顯示數千個錯誤頁面對咱們的用戶產生了切實的影響(還好 CDN 緩存抵消一部分影響)。html
被用戶看到的錯誤頁面前端
應用程序的錯誤日誌顯示,該應用程序沒有任何有關 top stories 的數據。java
首頁的工做原理是在一個時間間隔內輪詢 GraphQL api 以獲取數據,將該數據存儲在內存中並根據請求渲染它。從理論上講,若是請求失敗,應該保留以前穩定的數據。進一步深刻日誌,咱們看到對 GraphQL api 的請求失敗了,可是是有錯誤而不是超時——或者至少是不一樣類型的超時。node
FetchError: response timeout at https://….&source=next-front-page over limit: 5000
複製代碼
奇怪的是,API 的響應時間彷佛遠低於首頁設置的 5 秒超時。這讓咱們相信問題出如今首頁和應用程序之間的鏈接上。咱們作了些嘗試——在二者之間使用 keepAlive 鏈接,分散請求,這樣它們就不會同時發起。這些彷佛都沒有產生任何影響。android
更神祕的是 Heroku 上顯示的響應時間。第 95 百分位數約爲 2-3 秒,而最大值有時達到 10-15 秒。因爲首頁被 Fastly 高度緩存,包括 stale-while-revalidate 頭,許多用戶可能不會注意到。但這很奇怪,由於首頁真的不該該作不少工做來渲染頁面。全部數據都保存在內存中。ios
首頁的 Heroku 響應時間git
所以咱們決定對本地運行的應用程序副本進行一些分析。咱們將經過使用 Apache Bench 每秒發出10個請求,共發出 1000 個請求來複制一些負載。github
ab -n 1000 -c 10 http://local.ft.com:3002/
複製代碼
使用 node-clinic 和 nsolid,咱們能夠對內存、CPU 和應用程序代碼有更深的理解。運行它們,確認咱們能夠在本地復現該問題。首頁須要 200-300 s 才能完成測試,超過 800 個請求不成功。相比之下,在文章頁面上運行相同的測試須要大約 50 秒。後端
測試用時:305.629 秒
完成的請求:1000
失敗的請求:876
複製代碼
並且你看,n-solid 的圖表顯示事件循環的滯後超過 100 毫秒。
在作加載測試時事件循環滯後
使用 n-solid 的 CPU 分析器,咱們能夠精肯定位阻塞事件循環的確切代碼行。
火焰圖顯示致使滯後的函數
罪魁禍首是...
return JSON.parse(JSON.stringify(this._data));
複製代碼
對於每一個請求,咱們使用 JSON.parse/stringify 來建立數據的深克隆。這種方法自己不壞 —— 多是深克隆比較快方法之一。但它們是同步方法,所以在執行時會阻塞事件循環。
在咱們的案例中,這個方法在每一個頁面渲染(對於每一個被渲染的部分)中屢次調用,具備大量數據(每次執行時整個頁面所需的數據),而且咱們有幾個併發請求。因爲 Javascript 是單線程的,所以這將對應用程序嘗試執行的全部其餘操做產生連鎖反應。
深克隆數據的緣由是,咱們會根據請求中的一些信息(例如,是否啓用了特定功能的切換),來改變對象。
爲了解決這個問題——並減輕克隆全部內容的須要——咱們在檢索時對對象應用了深度凍結,而後在數據被改動的地方克隆特定位。這仍然執行同步克隆——但僅限於更小的數據子集。
修復好後,咱們從新運行了負載測試,並在很短的時間內完成,0 次錯誤。
測試用時:37.476 秒
完成的請求:1000
失敗的請求:0
複製代碼
咱們發佈了修復程序,看到響應時間和錯誤(🤞🏼)當即減小,並但願一些用戶開心!
修復後首頁的響應時間
感謝 Samuel Parkinson 的付出。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。