執行在 SSR 模式下的 Angular 應用的記憶體洩漏問題分析

i042416發表於2022-04-29

執行在 SSR 模式下的 Angular 應用,為了避免伺服器端和客戶端兩次呼叫同樣的 API 引起螢幕的 Flickering 問題,通過都會使用 Angular TransferState 服務將資訊從伺服器傳送到客戶端,其工作原理如下圖所示:

首先在應用程式 app.module.ts 中匯入 BrowserTransferStateModule

import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
imports: [
  BrowserModule.withServerTransition({appId: 'my-app'}),
  BrowserTransferStateModule,
  ...
]

然後在伺服器端模組 app.server.module.ts 中匯入 ServerTransferStateModule

import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
imports: [
  AppModule,
  ServerModule,
  ServerTransferStateModule,
  ...
]

我們可以使用 makeStateKey 函式來建立一個鍵,以儲存狀態中的資料(將傳遞給瀏覽器)。 使用 this.state.get 從狀態中獲取資料,並使用 this.state.set 設定狀態中的資料。 進行 API 呼叫時,使用之前呼叫 makeStateKey 建立的金鑰將返回的資料儲存在Angular state 中。

採用了 TransferState 服務的 Spartacus SSR 應用,在出現記憶體洩漏時通常有下列表現:

  • 使用者請求響應時間增加
  • jsapps pods 頻繁重啟
  • 執行時出現如下日誌:

(1) SSR rendering exceeded timeout 3000, fallbacking to CSR for /xyz
(2) PM2 Process 0 restarted because it exceeds --max-memory-restart value (current_memory=4009730048 max_memory_limit=3865051136 [octets])
(3) Rendering of /xyz was not able to complete. This might cause memory leaks!

如果出現了以上之一的症狀,我們可以使用 Dynatrace 進行記憶體洩漏原因分析。

在 Dynatrace 中,可以從左側導航選單中的技術和程式選項和 Node.js 技術中找到每個 SSR pod。 程式組將具有包含您的應用程式 SSR 的主檔案的名稱,通常是 main.js 或 server.js。 單擊後者將列出在指定時間範圍內處於活動狀態的 pod,允許我們訪問任何單個 pod 的程式詳細資訊頁面。

在 high level 層面,僅通過檢視 Dynatrace 中的 V8 堆記憶體圖表就可以對記憶體洩漏的可能性做出明智的判斷。 Node.js 中潛在記憶體洩漏的最明顯跡象是:

  1. V8 堆記憶體出現峰值(sharp spike)
  2. 每次 pod 重新啟動後,記憶體佔用圖都會再次出現峰值

通常情況下,如下圖所示的鋸齒模式(saw tooth)似乎表明應用程式健康,因為假設記憶體中的每個釋放都是由於垃圾收集而發生的。 但是,如果您發現記憶體中的每個峰值之後都會有一個記憶體釋放的行為,而該記憶體釋放僅僅是因為 pod 的重啟造成的,那麼該應用很可能存在記憶體洩漏的問題。

如果我們增加系統的可用記憶體,但是 Dynatrace 裡觀測到的鋸齒模式仍然存在,這種效能更能成為該應用存在記憶體洩漏的有力證據之一。

相關文章