Node.js 应用故障排查手册 —— 综合性 GC 问题和优化

楔子

本章前面两节生产案例分别侧重于单一的 CPU 高和单一的内存问题,我们也给大家详细展示了问题的定位排查过程,那么实际上还有一类相对更复杂的场景——它本质上是 V8 引擎的 GC 引发的问题。

简单的给大家介绍下什么是 GC,GC 实际上是语言引擎实现的一种自动垃圾回收机制,它会在设定的条件触发时(比如堆内存达到一定值)时查看当前堆上哪些对象已经不再使用,并且将这些没有再使用到的对象所占据的空间释放出来。许多的现代高级语言都实现了这一机制,来减轻程序员的心智负担。

本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。

GC 带来的问题

虽然上面介绍中现代语言的 GC 机制解放了程序员间接提升了开发效率,但是万事万物都存在利弊,底层的引擎引入 GC 后程序员无需再关注对象何时释放的问题,那么相对来说程序员也就没办法实现对自己编写的程序的精准控制,它带来两大问题:

  • 代码编写问题引发的内存泄漏
  • 程序执行的性能降低

内存泄漏问题我们已经在上一节的生产案例中体验了一下,那么后者是怎么回事呢?

其实理解起来也很简单:原本一个程序全部的工作都是执行业务逻辑,但是存在了 GC 机制后,程序总会在一定的条件下耗费时间在扫描堆空间找出不再使用的对象上,这样就变相降低了程序执行业务逻辑的时间,从而造成了性能的下降,而且降低的性能和耗费在 GC 上的时间,换言之即 GC 的次数 * 每次 GC 耗费的时间成正比。

问题现象与原始分析

现在大家应该对 GC 有了一个比较整体的了解,这里我们可以看下 GC 引发的问题在生产中的表现是什么样的。在这个案例中,表象首先是 Node.js 性能平台 上监控到进程的 CPU 达到 100%,但是此时服务器负载其实并不大,QPS 只有 100 上下,我们按照前面提到的处理 CPU 问题的步骤抓取 CPU Profile 进行分析可以看到:

这次的问题显然是 Garbage Collector 耗费的 CPU 太多了,也就是 GC 的问题。实际上绝大部分的 GC 机制引发的问题往往表象都是反映在 Node.js 进程的 CPU 上,而本质上这类问题可以认为是引擎的 GC 引起的问题,也可以理解为内存问题,我们看下这类问题的产生流程:

  • 堆内存不断达到触发 GC 动作的预设条件
  • 进程不断触发 GC 操作
  • 进程 CPU 飙高

而且 GC 问题不像之前的 ejs 模板渲染引发的问题,就算我们在 CPU Profile 中可以看到这部分的耗费,但是想要优化解决这个问题基本是无从下手的,幸运的是 Node.js 提供了(其实是 V8 引擎提供的)一系列的启动 Flag 能够输出进程触发 GC 动作时的相关日志以供开发者进行分析:

  • --trace_gc: 一行日志简要描述每次 GC 时的时间、类型、堆大小变化和产生原因
  • --trace_gc_verbose: 结合 --trace_gc 一起开启的话会展示每次 GC 后每个 V8 堆空间的详细状况
  • --trace_gc_nvp: 每一次 GC 的一些详细键值对信息,包含 GC 类型,暂停时间,内存变化等信息

加粗的 Flag 意味着我们需要在启动应用前加上才能在运行时生效,这部分的日志实际上是一个文本格式,可惜的是 Chrome devtools 原生并不支持 GC 日志的解析和结果展示,因此需要大家获取到以后进行对应的按行解析处理,当然我们也可以使用社区提供 v8-gc-log-parser 这个模块直接进行解析处理,对这一块有兴趣的同学可以看 @joyeeCheung 在 JS Interactive 的分享: Are Your V8 Garbage Collection Logs Speaking To You?,这里不详细展开。

更好的 GC 日志展示

虽然 Chrome devtools 并不能直接帮助我们解析展示 GC 日志的结果,但是 Node.js 性能平台 其实给大家提供了更方便的动态获取线上运行进程的 GC 状态信息以及对应的结果展示,换言之,开发者无需在运行你的 Node.js 应用前开启上面提到的那些 Flag 而仍然可以在想要获取到 GC 信息时通过控制台拿到 3 分钟内的 GC 数据。

对应在这个案例中,我们可以进入平台的应用实例详情页面,找到 GC 耗费特别大的进程,然后点击 GC Trace 抓取 GC 数据:

这里默认会抓取 3 分钟的对应进程的 GC 日志信息,等到结束后生成的文件会显示在 文件 页面:

此时点击 转储 即可上传到云端以供在线分析展示了,如下图所示:

最后点击这里的 分析 按钮,即可看到 AliNode 定制后的 GC 信息分析结果的展现:

结果展示中,可以比较方便的看到问题进程的 GC 具体次数,GC 类型以及每次 GC  的耗费时间等信息,方便我们进一步的分析定位。比如这次问题的 GC Trace 结果分析图中,我们可以看到红圈起来的几个重要信息:

  • GC 总暂停时间高达 47.8s,大头是 Scavenge
  • 3min 的 GC 追踪日志里面,总共进行了 988 次的 Scavenge 回收
  • 每次 Scavenge 耗时均值在 50 ~ 60ms 之间

从这些解困中我们可以看到此次 GC 案例的问题点集中在 Scavenge 回收阶段,即新生代的内存回收。那么通过翻阅 V8 的 Scavenge 回收逻辑可以知道,这个阶段触发回收的条件是:Semi space allocation failed

这样就可以推测,我们的应用在压测期间应该是在新生代频繁生成了大量的小对象,导致默认的 Semi space 总是处于很快被填满从而触发 Flip 的状态,这才会出现在 GC 追踪期间这么多的 Scavenge 回收和对应的 CPU 耗费,这样这个问题就变为如何去优化新生代的 GC 来提升应用性能。

优化新生代 GC

通过平台提供的 GC 数据抓取和结果分析,我们知道可以去尝试优化新生代的 GC 来达到提升应用性能的目的,而新生代的空间触发 GC 的条件又是其空间被占满,那么新生代的空间大小由 Flag --max-semi-space-size 控制,默认为 16MB,因此我们自然可以想到要可以通过调整默认的 Semi space 的值来进行优化。

这里需要注意的是,控制新生代空间的 Flag 在不同的 Node.js 版本下并不是一样的,大家具体可以查看当前的版本来进行确认使用。

在这个案例中,显然是默认的 16M 相对当前的应用来说显得比较小,导致 Scavenge 过于频繁,我们首先尝试通过启动时增加 --max-semi-space-size=64 这个 Flag 来将默认的新生代使用到的空间大小从 16M 的值增大为 64M,并且在流量比较大而且进程 CPU 很高时抓取 CPU Profile 观察效果:

调整后可以看到 Garbage collector 阶段 CPU 耗费占比下降到 7% 左右,再抓取 GC Trace 并观察其展示结果确认是不是 Scavenge 阶段的耗费下降了:

显然,Semi space 调大为 64M 后,Scavenge 次数从近 1000 次降低到 294 次,但是这种状况下每次的 Scavenge 回收耗时没有明显增加,还是在 50 ~ 60ms 之间波动,因此 3 分钟的 GC 追踪总的停顿时间从 48s 下降到 12s,相对应的,业务的 QPS 提升了约 10% 左右。

那么如果我们通过 --max-semi-space-size 这个 Flag 来继续调大新生代使用到的空间,是不是可以进一步优化这个应用的性能呢?此时尝试 --max-semi-space-size=128 来从 64M 调大到 128M,在进程 CPU 很高时继续抓取 CPU Profile 来查看效果:

此时 Garbage collector 耗费下降相比上面的设置为 64M 并不是很明显,GC 耗费下降占比不到 1%,同样我们来抓取并观察下 GC Trace 的结果来查看具体的原因:

很明显,造成相比设置为 64M 时 GC 占比提升不大的原因是:虽然此时进一步调大了 Semi space 至 128M,并且 Scavenge 回收的次数确实从 294 次下降到 145 次,但是每次算法回收耗时近乎翻倍了,因此总收益并不明显。

按照这个思路,我们再使用 --max-semi-space-size=256 来将新生代使用的空间进一步增大到 256M 再来进行最后一次的观察:

这里和调整为 128M 时是类似的情况: 3 分钟内 Scavenge 次数从 294 次下降到 72 次,但是相对的每次算法回收耗时波动到了 150ms 左右,因此整体性能并没有显著提升。

借助于性能平台的 GC 数据抓取和结果展示,通过以上的几次尝试改进 Semi space 的值后,我们可以看到从默认的 16M 设置到 64M 时,Node 应用的整体 GC 性能是有显著提升的,并且反映到压测 QPS 上大约提升了 10%;但是进一步将 Semi space 增大到 128M 和 256M 时,收益确并不明显,而且 Semi space 本身也是作用于新生代对象快速内存分配,本身不宜设置的过大,因此这次优化最终选取对此项目 最优的运行时 Semi space 的值为 64M

结尾

在本生产案例中,我们首先可以看到,项目使用的三方库其实也并不总是在所有场景下都不会有 Bug 的(实际上这是不可能的),因此在遇到三方库的问题时我们要敢于去从源码的层面来对问题进行深入的分析。

最后实际上在生产环境下通过 GC 方面的运行时调优来提升我们的项目性能是一种大家不那么常用的方式,这也有很大一部分原因是应用运行时 GC 状态本身不直接暴露给开发者。通过上面这个客户案例,我们可以看到借助于 Node.js 性能平台,实时感知 Node 应用 GC 状态以及进行对应的优化,使得不改一行代码提升项目性能变成了一件非常容易的事情。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519282.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“龙井”开箱评测 |Alibaba Dragonwell 新手上路指南

阿里巴巴有着最丰富的 Java 应用场景,覆盖电商,金融,物流等众多领域,是世界上最大的 Java 用户之一。 2019 年 3 月 21 日,阿里巴巴在北京阿里云峰会上正式宣布开源了 Alibaba Dragonwell 8 产品,并建立了 …

基于角色的访问控制(RBAC)

来源 | 编程新说责编 | Carol出品 | CSDN云计算(ID:CSDNcloud)很多时候,需要对一些事物进行控制,如一个房间,为了不让人随便进,通常会装一把锁,如果要想进入,你必须得有一…

win10 ie中没有java,win10没有ie浏览器怎么处理_window10找不到ie浏览器如何解决

很多用户升级到win10系统之后,发现默认浏览器是edge,想要使用ie浏览器的时候却发现没有ie浏览器,遇到window10找不到ie浏览器的话该怎么办呢,下面随小编一起来看看详细的解决步骤吧。方案一:1、直接搜索,右…

手把手教程:用Python开发一个自然语言处理模型,并用Flask进行部署

截住到目前为止,我们已经开发了许多机器学习模型,对测试数据进行了数值预测,并测试了结果。实际上,生成预测只是机器学习项目的一部分,尽管它是我认为最重要的部分。今天我们来创建一个用于文档分类、垃圾过滤的自然语…

干货|Spring Cloud Stream 体系及原理介绍

Spring Cloud Stream 在 Spring Cloud 体系内用于构建高度可扩展的基于事件驱动的微服务,其目的是为了简化消息在 Spring Cloud 应用程序中的开发。Spring Cloud Stream (后面以 SCS 代替 Spring Cloud Stream) 本身内容很多,而且它还有很多外部的依赖&a…

阿里小程序云应用上线了,有哪些看点?

3月21日,在2019阿里云峰会北京上,阿里巴巴旗下的阿里云、支付宝、淘宝、钉钉、高德等联合发布“阿里巴巴小程序繁星计划”:提供20亿元补贴,扶持200万小程序开发者、100万商家。凡入选“超星”的小程序,入驻支付宝、淘宝…

10 个实用功能告诉你,谷歌云(Google Cloud)相对亚马逊云(AWS)有哪些优势?...

来源 | itnext编译 | 武明利责编 | Carol出品 | CSDN云计算(ID:CSDNcloud)有很多文章将谷歌云提供商(GCP)与亚马逊云服务(AWS)进行比较,但这篇文章并不想要做比较。作者主要是一个AW…

mybatis-plus大批量数据插入缓慢问题

文章目录问题排查结果建议问题 最近项目用的mybatis-plus做的映射,有个批处理文件内容的需求,在使用mybatis-plus的批处理方法saveBatch时发现速度特别慢,测试从1000到10000到80000条基本上是线性增加,80000条时差不多要90秒。 …

世界冠军之路:菜鸟车辆路径规划求解引擎研发历程

阿里妹导读:车辆路径规划问题(Vehicle Routing Problem, VRP)是物流领域最经典的优化问题之一,具有极大的学术研究意义和实际应用价值。菜鸟网络高级算法专家胡浩源带领仓配智能化算法团队经过两年的研发,逐步沉淀出了…

原来,阿里工程师才是隐藏的“修图高手”!

阿里妹导读:在现实世界中,信息通常以不同的模态同时出现。这里提到的模态主要指信息的来源或者形式。例如在淘宝场景中,每个商品通常包含标题、商品短视频、主图、附图、各种商品属性(类目,价格,销量&#…

分布式数据集训营,从入门到精通,从理论到实践,你不可错过的精品课程!...

责编 | Carol出品 | CSDN云计算(ID:CSDNcloud)随着微服务、云化架构的兴起,分布式数据库开始在越来越多的场景得到应用,从外围系统到中台业务,再到核心交易业务,分布式数据库成为企业基础架构转…

mybatis批量插入10万条数据的优化过程

在使用mybatis插入大量数据的时候,为了提高效率,放弃循环插入,改为批量插入,mapper如下: package com.lcy.service.mapper;import com.lcy.service.pojo.TestVO; import org.apache.ibatis.annotations.Insert;import java.util.List;/*** 功能描述:** author liuc…

java spring注解维护,从一次工程启动失败谈谈 spring 注解

原标题:从一次工程启动失败谈谈 spring 注解檀宝权Java 后端开发工程师,负责度假 App 后端和广告后端开发维护工作,熟悉 Tomcat,Spring,Mybatis,会点 Python,Lua。一、背景线上环境升级成 JDK8后…

探索Java日志的奥秘:底层日志系统-log4j2

前言 log4j2是apache在log4j的基础上,参考logback架构实现的一套新的日志系统(我感觉是apache害怕logback了)。 log4j2的官方文档上写着一些它的优点: 在拥有全部logback特性的情况下,还修复了一些隐藏问题API 分离&…

大地震!某大厂“硬核”抢人,放话:只要AI人才,高中毕业都行!

特斯拉创始人马斯克,在2019年曾许下很多承诺,其中一个就是:2019年底实现完全的自动驾驶。虽然这个承诺又成了flag,但是不妨碍他今年继续为这个承诺努力。这不,就在上周一,马斯克之间在twitter上放话了&…

Dart编译技术在服务端的探索和应用

前言 最近闲鱼技术团队在FlutterDart的多端一体化的基础上,实现了FaaS研发模式。Dart吸取了其它高级语言设计的精华,例如Smalltalk的Image技术、JVM的HotSpot和Dart编译技术又师出同门。由Dart实现的语言容器,它可以在启动速度、运行性能有不…

Python + ElasticSearch:有了这个超级武器,你也可以报名参加诗词大会了! | 博文精选...

来源 | CSDN 博客作者 | 天元浪子责编 | Carol出品 | CSDN云计算(ID:CSDNcloud)意犹未尽的诗词大会正月十六,中国诗词大会第五季落下帷幕。从2016年2月12日第一季于开播,迄今恰好四周年。在这个舞台上,时年…

Node.js 应用故障排查手册 —— 大纲与常规问题指标简介

楔子 你是否想要尝试进行 Node.js 应用开发但是又总听人说它不安全、稳定性差,想在公司推广扩张大前端的能力范畴和影响又说服不了技术领导。 JavaScript 发展到今天,早已脱离原本浏览器的战场,借助于 Node.js 的诞生将其触角伸到了服务端、P…

蚂蚁金服CTO程立:做工程要有“拧螺丝”的精神

“一台机器可能有无数颗螺丝,需要一个一个地拧,而且需要一圈一圈地拧,才能让系统间严丝合缝,顺利工作。代码的世界里,一个项目到底成功与否,也是取决于几个模型的关键特殊设置,就像拧螺丝一样。…

linux 环境安装DBI和DBD_03

文章目录一、软件下载二、安装DBI2.1. DBI下载2.2. 解压2.3. 安装依赖2.4. 编译2.5. 执行测试2.6. 安装2.6. 修改权限三、安装DBD-ORACLE组件3.1. DBI下载3.2. 修改权限3.3. 切换用户3.4. 解压3.5. 进入目录3.6. 初始化环境变量3.6. 查看配置的环境变量是否配置3.7. 刷新配置文…