六年打怪升級,一路披荊斬棘,只為沒有難用的Node.js

摘要: 打磨六年,從0到1,Node.js性能平台免費護航阿里雲用戶。

作者:木環

原文:click.aliyun.com/m/4059

當頭棒喝的技術教訓

隨著近年社區的蓬勃發展,Node.js許多曾經的缺點都已經得到了改進;但相比其它在工業界有深厚積累和眾多實踐的企業級技術而言,Node.js還是一門年輕的技術,儘管使用者越來越多,可是真正涉足基礎技術的人員依然很少。

早在2012年,阿里巴巴也面臨著業界普遍遇到的問題。彼時,Node.js已經誕生了三年有餘,而阿里也使用近一年的Node.js。阿里雲的高級技術專家朴靈回憶道:「我以前端工程師身份加入淘寶,主要負責開發數據相關的Web產品,並在那時正式接觸Node.js服務端。作為這門新技術的狂熱粉絲,能夠開發並將其落地無疑是相當興奮激動的。當時有一個場景是要將前端數據發送到後端,通過Node.js記錄到MongoDB中。然而從那時候起,噩夢開始了。創建應用在正式上線運行後,每到3天內存都會增長並觸發報警直到線上重啟。」

彼時團隊不僅面臨著如何解決線上應用內存問題的巨大壓力,而且受到了為何使用新(不成熟)技術的質疑和挑戰。在重讀每行項目代碼、線下壓測之後,項目依然呈現線下ok但線上ko的詭異現象,期間只能採取定期重啟應用的過渡辦法。經過一段時間的寢食難安之後,團隊終於定位到前端的數據提交模塊,並發現是由於大量數據提交引發MongoDB調用堆積而導致的內存泄露。

「這件事情給了剛剛正式接觸服務端的我很深刻的印象,只要一想起就會隱隱作痛。」朴靈說道,「這也讓我意識到:『雖然我們可以遊刃有餘地構建Node.js應用,但是我們並沒有真正地馴服它。』」

也正是因此,朴靈開始轉向底層Node.js的研究。他說道:「如今,國內很很多Node.js的開發者,在面對線上問題時,他們會像我當初一樣手足無措。即使你JavaScript語言的使用輕車熟路,但是對於Node.js的底層——Chrome V8 引擎,前端工程師們幾乎一無所知。」

已有工具還不夠好,潛心研究底層技術

2014年,淘寶準備大力投入Node.js以改善上層應用的開發效率。而對於阿里巴巴而言,既然決定使用一門技術,則必然要能夠通透徹底地掌握該技術。就像阿里巴巴對JVM、操作系統內核、tengine等領域的投入一樣,投入研發資源並專門成立了Node.js底層技術團隊。

團隊至此正式地開啟了更底層技術的探索,但是,實話實說最開始時確實有些迷茫,團隊要做一些幫助大家排坑的事情,但是對於具體做什麼並不是瞭然於胸。團隊曾經花費三個月專註在性能優化,並做出了Node Profiler 工具,該工具可以深度挖掘CPU熱點問題及成因,可是它並不是剛需也不能很好地解決線上問題。

團隊一邊學習一邊摸索,在否決掉了一些並不成功的方向後,團隊確定了自己的使命:為Node.js技術提供各種支持保障,在開發、測試、監控、優化和解決故障等各個階段都幫助Node.js開發者。

對於業界而言,Node.js的疑難雜症分為三類:

  • 內存泄露
  • CPU飆高
  • GC停頓

彼時,也並非沒有工具可以解決:比如第一類內存泄露的問題,可以通過heapdum模塊生成內存堆快照,隨後通過Chrome瀏覽器的開發者工具進行查看和分析;第二類CPU飆高問題,可以通過 v8-profiler生成 CPU profile 文件,再通過開發者工具查看和分析;第三類GC停頓問題則可以通過--gc-trace 的 flag 來輸出 GC 日誌從而進行問題定位。

但是,這些工具的共同缺陷是:對線上應用非常不友好,無法即插即用,定位問題成本高。一個應用在線下表現正常,上線之後發生問題再進行工具安裝就為時已晚,還需要再修改上線後進行bug復現。而且,如果設置成異常時觸發生成文件以備分析,就會引發應用大規模運行時堆快照的陡然增多問題。

「我們需要做一點別人做不了的事情」。團隊從最初就在探索如何更好地解決Node.js應用的可診斷性問題,如何改變V8虛擬機的黑盒子情況。有兩件事情必須解決:

  • 深入了解Node.js運行時的內部狀態,思考如何使之對開發和運維人員更透明。
  • 在線上應用出現問題時,如何更好地解決問題,而非事到臨頭再找工具解決。

相應地,團隊決定重點攻克如何提高監控能力和診斷能力。

傳統的監控方案只能看到一些系統級別的信息,APM方案也不能提供徹底詳盡的問題報告。比如,雖然可以得到系統級CPU過高的信息,但是究竟是因為業務代碼的佔用還是因為GC的消耗,則不得而知。再比如,縱然APM方案可以知曉哪些API運行緩慢,但是故障原因是CPU還是內存這樣的進一步追問,並沒有得到回答。

團隊深入了解了Node.js內核,包括libuv和V8,並在一些地方進行埋點,通過定時日誌的方式將內核的狀態輸出出來,其中性能指標包括GC(新生代,老生代)、堆內存(整體、分空間)、句柄、定時器等,這些性能數據與監控系統結合,再搭配上報警判斷和消息通知,從而得到更深度、更透明的內核級別的性能監控。

在獲得了內核級的監控信息之後,就可以更加有針對性地排查問題。在內核層面,團隊提供了通過信號觸發內核進行Profiling的機制,並提供了一組腳本簡化操作。當收到內存可能泄露的信息時,可以找到對應進程,執行生成heapsnapshot的操作,當CPU飆高時生成cpuprofile、GC有異常時進行GC trace的操作、或者在更複雜的情況下組合操作互相配合。

從具體實現層面而言,雖然Node.js和Chrome都是基於V8,但是它並不能像Chrome一樣方便地實時診斷。以GC trace為例,V8可以通過flag方式輸出GC日誌;但是,flag是相當不友好的形式,除了進程啟動時開啟,其他情況下都是關閉的:如果默認進程都在啟動時開啟,會產生大量的無實際價值的GC日誌。可是,等發生問題的時候,為了開啟flag則只能重啟進程。因此,團隊改造GC trace功能,其開啟動作無需重啟進程,並且在一定時間後結束日誌的輸出。

「就好像生病時,醫生會通過儀器採集詳細的人體體征數據做出診斷。對於線上運行的Node.js應用,Node.js性能平台的可診斷特性通過『拍片』帶來了更詳盡的性能數據。診斷結束後,進程還能繼續運行。」朴靈解釋道,「Node.js的診斷性非常重要,它能夠留取運行時的現場,從而更好地分析問題。儘管通過重啟等操作,也能適當地緩解應用問題;但是,沒有問題現場,就無法從根本解決問題,而沒有解決的問題就如同炸彈一樣,會在某時再次出現。」

除了前面提到的監控能力和診斷能力之外,團隊還做到了更好的數據可視化呈現、分析問題演算法、甚至直接告知問題根源。

團隊充分考慮到了平台使用的易用性、穩定性和安全性:首先,自研的Node.js內核完全兼容開源社區的內核;其次,相比於傳統APM方案的插樁上報,平台日誌服務端完全不影響業務代碼的運行,即無侵入——建設一個並不影響內核的監控體系;除了必要部署作為最初準備,在分析問題時,用戶只需在界面上簡單操作而無需登錄伺服器。

產品打磨,讓天下沒有難解決的Node.js問題

團隊在2014年末完成了產品雛形,在隨後的一年中服務了集團內部的業務方,早先命名為Alinode並開始商業化的嘗試。彼時,業界尚無任何類似競品,直到兩年後NodeSource發布N|Solid。

Node.js在阿里巴巴內部應用相當成熟,目前已經成為僅次於Java的技術棧:除了1000+線上應用之外,還有Egg.js企業級框架、CNPM倉庫解決方案以及周邊數不清的業務支持中間件和SDK。而幾乎所有的Node.js業務,都由Node.js性能平台提供兜底的穩定性解決方案,日均為上萬台伺服器提供性能監控和診斷服務。

在談及產品定位時,朴靈說道:「我們始終注意著與社區Node.js版本保持同步一致,否則等於分裂社區。我們最重要的改進集中在穩定性方面,因為提供了深度監控和可診斷性,問題更容易地暴露和解決。」比如,在內存泄露的情況下,通過深度性能監控和在線profiling,可以將問題定位時間從1周左右降低到2-4個小時。

如今,產品已經更名為Node.js性能平台,並且正式對外提供商業化服務,所有阿里雲公共雲用戶均可免費使用。從內部使用到對外提供服務,Node.js性能平台團隊的客戶範圍變成是內部和外部的Node.js開發者。業界同領域產品目前只有IBM 的 Node.js SDK 和 NodeSource 的 N|Solid。在產品形態上,IBM 的產品附屬在其 BlueMix 產品線上, N|Solid 則須通過 license 或私有部署,而Node.js性能平台是SaaS化服務形態。用戶使用 Node.js 性能平台,不需要採購依賴的產品,也不需要進行部署,會更靈活。

值得一提的是,產品具有獨一無二的堆快照分析功能。這是因為引入了在 Java 領域裡非常成熟的 MAT,而不是繼續依賴 Chrome 的 devtools 工具。它不僅在演算法上更為先進,能夠直接得到問題可疑點;並且可以良好地適用一些極端情況的問題排查分析,比如一個 2GB 的堆快照(你很難想像怎麼在瀏覽器里分析堆快照,需要自己通過 devtools 進行分析,在龐大文件的茫茫對象中,找出有問題的那個。這如同大海撈針,是技術活,也是體力活。)。

安全,是更為重要的方面。平台安全功能分為兩塊,掃描漏洞和數據安全。平台並不會掃描用戶的代碼和環境,而只會檢查用戶所用的 Node 版本和模塊包的版本(通過配置 packages),如果版本存在安全問題則發出提醒。對於用戶的數據安全,Node.js 性能平台採集 Node.js 內核運行時狀態、各個空間的內存使用情況等信息,而沒有涉及到業務層面。用戶的日誌只有用戶的阿里雲賬號可見,需要協助時必須要授權。

Case1: 應用的穩定性,更高效更安全的runtime

這裡有一個實際客戶的案例。它的 GC 佔比時間達到極其誇張的 43%,這意味著3分鐘的採樣時間裡,有 43% 的時間,應用程序都在做 GC 操作。

平均每次的停頓時間在 30ms ~ 80ms 之間。再看詳細的情況,可以看到新生代 GC(Scavenge GC)極其頻繁:

再對比老生代 GC(Mark-sweep GC),我們發現這樣的現象,老生代 GC 的次數相當少,但是每次回收的內存都非常大,而新生代 GC 儘管頻繁,但停頓時間與老生代 GC 幾乎相同,並且,每次只能回收少許的內存。也就是說新生代 GC 的表現比老生代 GC 更差。

當然這個原因主要是由於系統中存在大對象導致的:

從 heapsnapshot 上也能驗證這個問題:

為了解決客戶所遇到的這個問題,我們的思路是既然新生代 GC 不夠好,那我們希望能夠減少不必要的新生代 GC,也就是減少新生代 GC 的次數。在相同的內存分配頻率下,新生代 GC 的頻率跟新生代空間的大小有一定關係,因此,只要增加新生代空間,就可以減少新生代次數。

我們讓客戶在啟動進程的地方加上 --max_semi_space_size=64 ,後來監控情況下,極端 GC 停頓最多只達到 20%,正常情況下,均低於5%。

再看改造之後的表現圖:

我們將新生代/老生代的佔比從 1375 / 10 降低到 134 / 14,GC 停頓降低到 3%,滿足正常的預期。

Case2: 解決令人頭疼的內存泄露問題

內存泄漏是指沒有被正確回收的內存,當一個系統中沒有被正確回收的內存逐漸增多時,系統的有效內存(能正常申請和回收)就會越來越少。對外的表現則是進程的內存佔用會越來越多:

通過 Node.js 性能平台的堆快照生成功能,及分析,可以直接得到分析報告:

展開對象,可以看到更多細節:

最終,通過堆快照分析給出的一些關鍵字:Application、fengdie、symbol。可以從業務代碼中迅速定位到問題。

Case3: 高效使用CPU,以便擴容

在一次幫助外部客戶壓測是發現了較高的CPU佔用,通過平台生成含有有效信息的CPU profile文件。通過查看堆棧,看到 Writable.onwrite->clearBuffer 調用鏈路上佔了 25% 的 CPU 資源,這個調用鏈路下,是 log4js 的操作。結合到當時生成的 heapsnapshot 文件,可以判定是寫入日誌的負載較重,發現客戶記錄了所有的生產日誌。經過梳理,去掉不必要的日誌,最終相應的 CPU 佔用降低到了 6-7% 左右。

在上述過程中,Node.js性能平台配合使用了PTS進行壓測。PTS 的壓測可以看到在預計高流量時系統性能是否符合預期,是否存在問題;隨後再通過 Node.js 性能平台,解決壓測過程中遇到的瓶頸問題。

業務開發同學一般更關注的是如何實現業務邏輯,對底層技術則涉獵不多,如果要解決一些瓶頸問題或奇怪的疑難問題,則相當費時費力。Node.js 性能平台可以彌補企業級應用層面的薄弱環節,能夠節省解決問題的時間和精力,讓企業專註在自己的業務開發中。

砥礪前行,打磨更好的產品

除了前文提到的問題,還有一些問題算是業界比較難以解決的,比如死循環、堆外內存泄漏以及剖屍診斷(對死掉的進程進行原因分析)等等。這些都是團隊正在攻克的難題,並會在不久的將來把對應的解決方案以產品功能的形式提供出來。團隊會繼續在增強內核能力上投入資源,並會持續在使用體驗上做出改進。

如今,Node.js 性能平台免費供所有阿里雲用戶使用。這意味著阿里雲上的所有 Node.js 用戶可以受益於阿里內部在過去幾年所沉澱下來的在 Node.js 領域的穩定性經驗,遠離那些令人飽受折磨的各種疑難雜症,從而更加專註於自有業務開發。

未來,Node.js 性能平台會往更智能化的方向前進。理想效果是,接入 Node.js 性能平台後,能自動幫助用戶分析線上的潛在問題,提前提醒用戶進行 Profiling,Profiling 之後,能根據性能文件自動定位出哪一行代碼出了問題。並且,專有雲和國際化也是產品未來發展方向之一。在服務層面上,團隊期望為更多的用戶提供更好的服務。目前產品上提供的工具、功能和服務,在公共雲上均是免費的,團隊未來也會推出專有雲和專家諮詢服務,不過相比維護SaaS化站點此類服務需要更多的人力成本。

「讓天下沒有難解決的 Node.js 問題。」 這,是Node.js性能平台團隊的唯一目標。

阿里雲於近日推出了Node.js性能平台,旨在為Node.js應用提供集性能監控、性能優化和故障排查為一體的SaaS化服務。更為重要的是,阿里雲公共雲用戶可免費使用該平台的全部功能。更多Node.js性能平台信息,可以移步 cn.aliyun.com/product/n

更多技術乾貨敬請關注云棲社區知乎機構號:阿里云云棲社區 - 知乎

推薦閱讀:

動態追蹤技術:trace your kernel Functions!
雜說快閃記憶體番外:手機為什麼越用越卡和快閃記憶體寫放大
golang, node.js 與 python 文件處理性能測試

TAG:编程语言 | 监控 | 性能 |