本文發表於 北斗同構github, 轉載請註明出處
注: 本文爲第12屆D2前端技術論壇《打造高可靠與高性能的React同構解決方案》分享內容,已通過數據脫敏處理。javascript
菜鳥物流大市場是菜鳥旗下的一條業務線,能夠簡單地理解爲物流領域的淘寶,是爲撮合物流需求方和物流提供方搭建的一個平臺。其中搜索頁、詳情頁、買家中心等頁面是基於beidou同構框架開發的。隨着node、react同構等技術愈來愈普遍地使用, 內存泄漏的事情時有發生,應當引發足夠的重視。最近在作菜鳥物流市場的技術支持,就「中獎」了,把實踐過程當中的經驗和心得整理了下,供你們參考。前端
先介紹幾個基本術語:java
回到以前說到的菜鳥物流大市場node
菜鳥物流大市場上線以後,常常收到alimonitor的告警通知,以下圖react
因而打開了alinode查看慢日誌, 果真有很多慢日誌記錄git
當時主要有如下幾個現象:github
根據當時的現象作了簡單的分析並制定了具體的action:web
從上面的推斷來看,發生內存泄漏的可能性很是大,但仍然須要經過實際數據進行驗證,因而根據制定的action進行數據採集chrome
再次發佈以後,採集到了數據:api
從上圖中能夠看出, 隨着時間的推移,進程1694的hsf調用耗時始終穩定,可是服務端渲染的時間卻逐步飆升到3700多毫秒,而後在某個臨界值以後瞬間下降到50毫秒左右。多是因爲某某事件( 猜想是內存泄漏引發OOM )致使了進程崩潰,接下來beidou框架會自動重啓進程又恢復良好的狀態。打開sandbox一看進程生命週期,果真如此, 進程1694掛了,而後從新啓動了一個29649進程。
從上圖中也能夠看到RSS(實際使用物理內存)高達1880.93MB,至此基本上能夠肯定是內存泄漏了。查看內存佔用曲線,內存呈現鋸齒狀,先一路飆升,到達零界點以後瞬間降低,如此周而復始。和咱們的推斷徹底一致,這是典型的內存泄漏曲線。
最終結論: 訪問速度慢是由於內存泄漏消耗了過多的資源
定位到是內存泄漏以後,還須要進一步排查具體是什麼代碼致使了內存泄漏。這時候就要用到排查神器 - alinode了。
先建立堆快照:
在分析頁面打開對象簇視圖
, 能夠看到裏面有大量的Window對象, 搜索下居然高達390個
採樣了幾個Window對象,經過GC Root
展開,發現掛載了無數個定時器。
分析代碼找到了兩處定時器的設置,看代碼邏輯,該定時器在服務端根本不會被釋放。
componentWillMount(){ let _this = this; window.handler = window.setInterval(function(){ if(typeof AMap){ _this.renderMap('', AMap); window.clearInterval(window.handler); } }, 300); }
註釋掉以後在預發驗證沒有再出現window相關的內存泄漏。
PS.
後來的驗證發現,除了定時器的問題,還有另外兩處內存泄漏,再也不贅述, 貼上其中一處(高德地圖)內存泄漏的代碼供讀者參考
componentWillMount(){ this.createAmapScript(); } createAmapScript(){ let script = document.createElement('script'), body = document.getElementsByTagName('body')[0]; script.type = 'text/javascript'; script.src = 'https://webapi.amap.com/maps?v=1.3&key=59699a8cfee7c52f58390357cbdbf27d'; body.appendChild(script); }
從上述兩處代碼能夠看出,定時器無需在服務端執行, 而高德地圖自己就不支持服務端渲染,所以可將兩者放到客服端渲染便可。根據react的特性,componentDidMount生命週期函數在服務端不會執行,所以將上述代碼從componentWillMount移到componentDidMount中便可。具體修復以下:
經過loadtest在本地壓測驗證下:
單個進程一樣以10個QPS進行施壓,對比下能夠看出,修復前RT時間一路上升,而修復後RT始終穩定在200毫秒左右。
再看看線上數據, 內存佔用率始終穩定,沒有出現飆升現象。
至此,打完收工。
看完了案例,是時候系統化地總結下方法論了。
從剛纔的案例中能夠看出來,內存泄漏最典型的現象就是內存佔用率會隨着時間的推移而逐步上升,就算沒有流量了,內存佔用率也不會降低。而健康的應用是流量上升內存佔用會上升,而流量降低以後內存佔用率就會回到原水平。
一般形成內存泄漏的有如下幾個因素
本文中的案例就屬於做用域未釋放
本地
線上
上醫治未病,中醫治欲病,下醫治已病
,說的是醫術最高明的醫生並非擅長治病的人,而是可以預防疾病的人。讓問題在開發階段就暴露出來, 而不是等到線上告警了再搶救。constructor
中作事件綁定,建議放到componentDidMount生命週期中