首頁  >  文章  >  web前端  >  為什麼需要效能監控?聊聊Node.js效能監控

為什麼需要效能監控?聊聊Node.js效能監控

青灯夜游
青灯夜游轉載
2022-08-11 20:14:272232瀏覽

為什麼需要效能監控?以下這篇就來帶大家了解一下Node.js效能監控,希望對大家有幫助!

為什麼需要效能監控?聊聊Node.js效能監控

為什麼需要效能監控

#Node作為Javascript在服務端的一個執行時間(Runtime),極大的豐富了Javascript的應用場景。

但是Node.js Runtime本身就是一個黑盒,我們無法感知運行時的狀態,對於線上問題也難以復現

因此效能監控是Node.js應用程式「正常運作」的基石。不僅可以隨時監控執行時期的各項指標,還可以幫助排除異常場景問題。

組成部分

效能監控可以分為兩個部分:

  • 效能指標的擷取與展示

    • 進程層級的資料:CPU,Memory,Heap,GC等
    • 系統層級的資料:磁碟佔用率,I/O負載,TCP/UDP連線狀態等
    • #應用層的資料:QPS,慢HTTP,業務處理鏈路日誌等
  • #效能資料的抓取和分析

    • Heapsnapshot:堆記憶體快照
    • Cpuprofile:CPU快照
    • Coredump:套用崩潰快照

為什麼需要效能監控?聊聊Node.js效能監控

  • ##從上圖可以看到目前主流的三種Node.js效能監控方案的優缺點,以下是簡單介紹這三種方案的組成:
    • Prometheus
      • prom-client
      • 是prometheus的nodejs實現,用於採集效能指標
      grafana
    • 是一個視覺化平台,用來展示各種資料圖表,支援prometheus的存取
    • 只支援了效能指標的擷取與展示,排查問題還需要其他快照工具,才能組成閉迴路

      • AliNodealinode是一個相容官方nodejs的拓展運行時,提供了一些額外功能:
      ##v8的執行時期記憶體狀態監控
    • libuv的執行階段狀態監控

      #線上故障診斷功能:堆疊快照、CPU Profile、GC Trace等
  • agenthub是常駐進程,用來收集效能指標並回報

    • 整合了agentx
    • commdx
    • 的便利工具
    • 整體從監控,展示,快照,分析形成閉環,接入便捷簡單,但是拓展運行時還是有風險

Easy-Monitor

#xprofiler

負責進行即時的執行時間狀態取樣,以及輸出效能日誌(也就是效能數據的抓取)

為什麼需要效能監控?聊聊Node.js效能監控

xtransit

負責效能日誌的擷取與傳輸跟AliNode最大的差別在於使用了

Node.js Addon
    來實現採樣器

效能指標

#CPU為什麼需要效能監控?聊聊Node.js效能監控

透過
    process.cpuUsage()
  • 可以取得目前進程的CPU耗時數據,回傳值的單位是微秒
  • ##user:進程執行時本身消耗的CPU時間
  • system:進程執行時系統消耗的CPU時間
  • Memory

為什麼需要效能監控?聊聊Node.js效能監控

###透過# ##process.memoryUsage()###可以取得目前進程的記憶體分配數據,回傳值的單位是位元組#########rss:常駐內存,node程序分配的總記憶體大小## ####heapTotal:v8申請的堆記憶體大小######heapUsed:v8已使用的堆記憶體大小######external:v8管理的C 所佔用的記憶體大小###### arrayBuffers:分配給ArrayBuffer的記憶體大小################

從上圖可以看出,rss包含程式碼段(Code Segment)、堆疊記憶體(Stack)、堆記憶體(Heap )

  • Code Segment:儲存程式碼片段
  • Stack:儲存局部變數和管理函數呼叫
  • Heap:儲存物件、閉包、或其他一切

Heap

透過v8.getHeapStatistics()v8.getHeapSpaceStatistics()可以取得v8堆內存和堆空間的分析數據,下圖展示了v8的堆內存組成分佈:

為什麼需要效能監控?聊聊Node.js效能監控

堆內存空間先劃分為空間(space),空間又劃分為頁(page),記憶體依照1MB對齊進行分頁。

  • New Space:新生代空間,用來存放一些生命週期比較短的物件數據,平分為兩個空間(空間類型為semi space): from spaceto space

    • 升遷條件:在New space中經過兩次GC依舊存活

Old Space:老生代空間,用來存放

New Space

晉升的物件為什麼需要效能監控?聊聊Node.js效能監控

Code Space:存放v8 JIT編譯後的可執行程式碼Map Space:存放Object指向的隱藏類別的指標目標,隱藏類別指標是v8根據執行時間記錄下的物件佈局結構,用於快速存取物件成員

Large Object Space:用來存放大於1MB而無法分配到頁的物件

    GC
  • ##v8的垃圾回收演算法分為兩類:

    Major GC:使用了
  • Mark-Sweep-Compact
  • 演算法,用於老生代的物件回收

    Minor GC :使用了
      Scavenge
    • 演算法,用於新生代的物件回收
    • Scavenge
  • 前提:

    New space分為fromto

    兩個物件空間
  • 觸發時機:當

    New space空間滿了步驟:

  • from space
中,進行寬度優先遍歷

發現存活(可達)物件

已經存活過一次(經歷過一次Scavange),晉升到

Old space

為什麼需要效能監控?聊聊Node.js效能監控

其他的複製到

to space

當複製結束時,

to space
    中只有存活的對象,
  • from space

    就被清空了

    • 交換
    • from space
    • to space
    • ,開始下一輪
    • Scavenge
    • 適用於回收頻繁,記憶體不大的對象,典型的空間換時間的策略,缺點是浪費了多一倍的空間
    • Mark- Sweep-Compact
    • 三個步驟:標記、清除、整理
    • 觸發時機:當
    • Old space
    空間滿了
  • 步驟:

    • Marking(三色標記法)
    • 白色:代表可回收物件
  • 黑色:代表不可回收對象,且其所產生的引用都已經掃描完畢
  • 灰色:代表不可回收對象,且其所產生的引用還沒掃描完

    ###將V8根對象直接引用的物件放進一個###marking queue###(顯式堆疊)中,並將這些物件標記為灰色######從這些物件開始做深度優先遍歷,每存取一個對象,將該物件從###marking queue### ###pop###出來,並標記為黑色######然後將該物件引用下的所有白色物件標記為灰色,###push###到###marking queue###上,如此往復######直到堆疊上所有物件都pop掉為止,老生代的物件只剩下黑色(不可回收)和白色(可以回收)兩種了######PS:當一個物件太大,無法push到空間有限的堆疊時,v8會把這個物件保留灰色跳過,將整個堆疊標記為溢位狀態(overflowed),等堆疊清空後,再次進行遍歷標記,這樣導致需要額外掃描一遍堆###############Sweep#########清除白色物件######會造成記憶體空間不連續###############Compact###
    • 由於Sweep會造成記憶體空間不連續,不利於新物件進入GC
    • 把黑色(存活)物件移到Old space的一端,這樣清除出來的空間就是連續完整的
    • 雖然可以解決記憶體碎片問題,但是會增加停頓時間(執行速度慢)
    • 在空間不足以對新生代晉升過來的物件進行分配時才使用mark-compact

Stop-The-World

在最開始v8進行垃圾回收時,需要停止程式的運行,掃描完整個堆,回收完內存,才會重新執行程式。這種行為就叫全停頓(Stop-The-World

雖然新生代活動對象較小,回收頻繁,全停頓,影響不大,但是老生代存活對像多且大,標記、清理、整理等造成的停頓就會比較嚴重。

優化策略

  • 增量回收(Incremental Marking):在Marking階段,當堆達到一定大小時,開始增量GC,每次分配了一定量的記憶體後,就暫停執行程序,做幾毫秒到幾十毫秒的marking,然後再恢復程式的運作。

這個理念其實有點像React框架中的Fiber架構,只有在瀏覽器的空閒時間才會去遍歷Fiber Tree執行對應的任務,否則延遲執行,盡可能少地影響主線程的任務,避免應用卡頓,提升應用效能。

  • 並發清除(Concurrent Sweeping):讓其他執行緒同時來做sweeping,而不用擔心和執行程式的主執行緒衝突
  • 並行清除(Parallel Sweeping):讓多個Sweeping 執行緒同時運作,提升sweeping 的吞吐量,縮短整個GC 的週期

#空間調整

由於v8對於新老生代的空間預設限制了大小

  • New space 預設限制:64位元系統為32M,32位元系統為16M
  • Old space# 預設限制: 64位元系統為1400M,32位元系統為700M

因此node提供了兩個參數用於調整新老生代的空間上限

  • #--max-semi-space-size:設定New Space空間的最大值
  • ##--max-old-space-size:設定Old Space空間的最大值

查看GC日誌

##node

查看GC日誌

  • ##node也提供了三種檢視GC日誌的方式:
  • --trace_gc:一行日誌簡單描述每次GC時的時間、型別、堆大小變更和產生原因
  • --trace_gc_verbose:展示每次GC後每個V8堆空間的詳細狀況

--trace_gc_nvp:每次GC的詳細鍵值對訊息,包含GC類型,暫停時間,記憶體變化等

由於GC日誌比較原始,還需要二次處理,可以使用AliNode團隊開發的v8-gc-log-parser

快照工具

Heapsnapshot

對於執行程式的堆記憶體進行快照取樣,可以用來分析記憶體的消耗以及變化

產生方式

    #產生
  • .heapsnapshot

    檔案有以下幾種方式:

  • 使用
heapdump

為什麼需要效能監控?聊聊Node.js效能監控

  • #使用v8的
heap- profile

為什麼需要效能監控?聊聊Node.js效能監控

    • #使用nodejs內建的v8模組提供的api

    v8.getHeapSnapshot()

    1為什麼需要效能監控?聊聊Node.js效能監控

    v8.writeHeapSnapshot(fileName)

    1為什麼需要效能監控?聊聊Node.js效能監控

  • 使用
v8-profiler-next

1為什麼需要效能監控?聊聊Node.js效能監控

分析方法

產生的1為什麼需要效能監控?聊聊Node.js效能監控.heapsnapshot

文件,可以在Chrome devtools工具列的Memory,選擇上傳後,展示結果如下圖:############

預設的視圖是Summary視圖,在這裡我們要關注最右邊兩欄:Shallow SizeRetained Size

  • Shallow Size:表示該物件本身在v8堆記憶體分配的大小
  • #Retained Size:表示該物件所有引用物件的Shallow Size之和

當發現Retained Size特別大時,該物件內部可能存在記憶體洩漏,可以進一步展開去定位問題

還有Comparison視圖是用來比較分析兩個不同時段的堆快照,透過Delta列可以篩選出記憶體變化最大的物件

1為什麼需要效能監控?聊聊Node.js效能監控

Cpuprofile

對於執行程式的CPU進行快照取樣,可以用來分析CPU的耗時及佔比

##產生方式

產生

.cpuprofile檔案有以下幾種方式:

    ##v8-profiler
  • (node官方提供的工具,不過已經無法支援node v10以上的版本,並不再維護)
  • v8-profiler-next
  • (國人維護版本,支援到最新node v18,持續維護中)
  • 這是採集5分鐘的CPU Profile範例

為什麼需要效能監控?聊聊Node.js效能監控

分析方法

產生的

.cpuprofile

文件,可以在Chrome devtools工具列的Javascript Profiler(不在預設tab,需要在工具列右側的更多中開啟顯示),選擇上傳文件後,展示結果如下圖:

為什麼需要效能監控?聊聊Node.js效能監控預設的視圖是

Heavy

視圖,在這裡我們看到有兩欄:Self TimeTotal Time

    Self Time
  • :代表此函數本身(不包含其他呼叫)的執行耗時
  • Total Time
  • :代表此函數(包含其他呼叫函數)的總執行耗時
  • 當發現
Total Time

Self Time偏差較大時,此函數可能存在耗時比較多的CPU密集型運算,也可以展開進一步定位排查

#Codedump

當應用程式意外崩潰終止時,系統會自動記錄下進程crash掉那一刻的記憶體分配信息,Program Counter以及堆疊指標等關鍵資訊來產生core檔

##產生方式

產生.core

檔案的三種方法:

ulimit -c unlimited
    開啟核心限制
  • node --abort-on-uncaught-exception
  • node啟動加入此參數,可以在套用出現未捕獲的例外狀況時也能產生一份core檔
  • #gcore
  • 手動產生core檔
  • 分析方法

取得.core

檔案後,可以透過mdb、gdb、lldb等工具實現解析診斷實際進程crash的原因

llnode `which node` -c /path/to/core/dump
  • ## 案例分析

為什麼需要效能監控?聊聊Node.js效能監控

##觀察1為什麼需要效能監控?聊聊Node.js效能監控

從監控可以觀察到堆內存在持續上升,因此需要堆快照進行排查

分析

為什麼需要效能監控?聊聊Node.js效能監控

根據

heapsnapshot可以分析排查到有一個newThing的物件一直保持著比較大的記憶體排查

從程式碼可以看到雖然

unused

方法沒有調用,但是

newThing# ##物件是引用自###theThing###,導致其一直存在於###replaceThing###這個函數的執行上下文中,沒有被釋放,這就是典型的由於閉包產生的記憶體洩漏案例# ########小結#########常見的記憶體洩漏有以下幾種情況:###
  • 全域變數
  • 閉包
  • 計時器
  • 事件監聽
  • 快取

因此在上述這幾種情況時,一定要謹慎考慮物件在記憶體中是否會被自動回收,不會被自動回收的話,需要手動進行回收,例如手動把物件設定為null、移除定時器、解綁事件監聽等

總結

至此,本文已經對整個Node.js的效能監控系統進行了詳細的介紹。

首先,介紹了效能監控解決的問題,組成部分以及主流方案的優缺點對比。

然後,針對兩大部分效能指標和快照工具進行了具體的介紹,

  • 效能指標主要關注CPU、記憶體、堆空間、GC幾個指標,同時介紹了v8的GC策略和GC最佳化方案,
  • 快照工具主要有堆快照、CPU快照以及崩潰時的Coredump

最後,從觀察、分析、檢查再現一個簡單的記憶體洩漏案例,並總結了常見記憶體洩漏的情況和解決方案。

希望這篇文章能幫助大家對整個Node.js的效能監控系統有所了解。

更多node相關知識,請造訪:nodejs 教學

以上是為什麼需要效能監控?聊聊Node.js效能監控的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除