运行在 SSR 模式下的 Angular 应用的内存泄漏问题分析
运行在 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][WORKER] 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 中潜在内存泄漏的最明显迹象是:
-
V8 堆内存出现峰值(sharp spike)
-
每次 pod 重新启动后,内存占用图都会
再次
出现峰值
通常情况下,如下图所示的锯齿模式(saw tooth
)似乎表明应用程序健康,因为假设内存中的每个释放都是由于垃圾收集而发生的。 但是,如果您发现内存中的每个峰值之后都会有一个内存释放的行为,而该内存释放仅仅是因为 pod 的重启造成的,那么该应用很可能存在内存泄漏的问题。
如果我们增加系统的可用内存,但是 Dynatrace 里观测到的锯齿模式仍然存在,这种性能更能成为该应用存在内存泄漏的有力证据之一。