Node.js 应用故障排查手册 —— 利用 CPU 分析调优吞吐量

楔子

在我们想要新上线一个 Node.js 应用之前,尤其是技术栈切换的第一个 Node.js 应用,由于担心其在线上的吞吐量表现,肯定会想要进行性能压测,以便对其在当前的集群规模下能抗住多少流量有一个预估。本案例实际上正是在这样的一个场景下,我们想要上线 Node.js 技术栈来做前后端分离,那么刨开后端服务的响应 QPS,纯使用 Node.js 进行的模板渲染能有怎么样的表现,这是大家非常关心的问题。

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

优化过程

集群在性能压测下反映出来的整体能力其实由单机吞吐量就可以测算得知,因此这次的性能压测采用的 4 核 8G 内存的服务器进行压测,并且页面使用比较流行的 ejs 进行服务端渲染,进程数则按照核数使用 PM2 启动了四个业务子进程来运行。

I. 开始压测

完成这些准备后,使用阿里云提供的 PTS 性能压测工具进行压力测试,此时大致单机 ejs 模板渲染的 QPS 在 200 左右,此时通过 Node.js 性能平台 监控可以看到四个子进程的 CPU 基本都在 100%,即 CPU 负载达到了瓶颈,但是区区 200 左右的 QPS 显然系统整体渲染非常的不理想。

II. 模板缓存

因为是 CPU 达到了系统瓶颈导致整体 QPS 上不去,因此按照第二部分工具篇章节的方法,我们在平台上抓了 压测期间 的 3 分钟的 CPU Profile,展现的结果如下图所示:

这里就看到了很奇怪的地方,因为压测环境下我们已经打开了模板缓存,按理来说不会再出现 ejs.compile 函数对应的模板编译才对。仔细比对项目中的渲染逻辑代码,发现这部分采用了一个比较不常见的模块 koa-view,项目开发者想当然地用 ejs 模块地入参方式传入了 cache: true,但是实际上该模块并没有对 ejs 模板做更好的支持,因此实际压测情况下模板缓存并没有生效,而模板地编译动作本质上字符串处理,它恰恰是一个 CPU 密集地操作,这就导致了 QPS 达不到预期的状况。

了解到原因之后,首先我们将 koa-view 替换为更好用的 koa-ejs 模块,并且按照 koa-ejs 的文档正确开启缓存:

render(app, {root: path.join(__dirname, 'view'),viewExt: 'html',cache: true
});

再次进行压测后,单机下的 QPS 提升到了 600 左右,虽然大约提升了三倍的性能,但是仍然达不到预期的目标。

III. include 编译

为了继续优化进一步提升服务器的渲染性能,我们继续在压测期间抓取 3 分钟的 CPU Profile 进行查看:

可以看到,我们虽然已经确认使用 koa-ejs 模块且正确开启了缓存,但是压测期间的 CPU Profile 里面竟然还有 ejs 的 compile 动作!继续展开这里的 compile,发现是 includeFile 时引入的,继续回到项目本身,观察压测的页面模板,确实使用了 ejs 注入的 include 方法来引入其它模板:

<%- include("../xxx") %>

对比 ejs 的源代码后,这个注入的 include 函数调用链确实也是 include -> includeFile -> handleCache -> compile,与压测得到的 CPU Profile 展示的内容一致。那么下面红框内的 replace 部分也是在 compile 过程中产生的。

到了这里开始怀疑 koa-ejs 模块没有正确地将 cache 参数传递给真正负责渲染地 ejs 模块,导致这个问题地发生,所以继续去阅读 koa-ejs 的缓存设置,以下是简化后的逻辑(koa-ejs@4.1.1 版本):

const cache = Object.create(null);async function render(view, options) {view += settings.viewExt;const viewPath = path.join(settings.root, view);// 如果有缓存直接使用缓存后的模板解析得到的函数进行渲染if (settings.cache && cache[viewPath]) {return cache[viewPath].call(options.scope, options);}// 没有缓存首次渲染调用 ejs.compile 进行编译const tpl = await fs.readFile(viewPath, 'utf8');const fn = ejs.compile(tpl, {filename: viewPath,_with: settings._with,compileDebug: settings.debug && settings.compileDebug,debug: settings.debug,delimiter: settings.delimiter});// 将 ejs.compile 得到的模板解析函数缓存起来if (settings.cache) {cache[viewPath] = fn;}return fn.call(options.scope, options);
}

显然,koa-ejs 模板的模板缓存是完全自己实现的,并没有在调用 ejs.compile 方法时传入的 option 参数内将用户设置的 cache 参数传递过去而使用 ejs 模块提供的 cache 能力。但是偏偏项目在模板内又直接使用了 ejs 模块注入的 include 方法进行模板间的调用,产生的结果就是只缓存了主模板,而主模板使用 include 调用别的模板还是会重新进行编译解析,进而造成压测下还是存在大量重复的模板编译动作导致 QPS 升不上去。

再次找到了问题的根源,为了验证是否是 koa-ejs 模块本身的 bug,我们在项目中将其渲染逻辑稍作更改:

const fn = ejs.compile(tpl, {filename: viewPath,_with: settings._with,compileDebug: settings.debug && settings.compileDebug,debug: settings.debug,delimiter: settings.delimiter,// 将用户设置的 cache 参数传递给 ejs 而使用到其提供的缓存能力cache: settings.cache
});

然后打包后进行压测,此时单机 QPS 从 600 提升至 4000 左右,基本达到了上线前的性能预期,为了确认压测下是否还有模板的编译动作,我们继续在 Node.js 性能平台 上抓取压测期间 3 分钟的 CPU Profile:

可以看到上述对 koa-ejs 模板进行优化后,ejs.compile 确实消失了,而压测期间不再有大量重复且耗费 CPU 的编译动作后,应用整体的性能比最开始有了 20 倍左右的提升。文中 koa-ejs 模块缓存问题已经在 4.1.2 版本(包含)之后被修复了,详情可以见 cache include file,如果大家使用的 koa-ejs 版本 >= 4.1.2 就可以放心使用。

结尾

CPU Profile 本质上以可读的方式反映给开发者运行时的 JavaScript 代码执行频繁程度,除了在线上进程出现负载很高时能够用来定位问题代码之外,它在我们上线前进行性能压测和对应的性能调优时也能提供巨大的帮助。这里需要注意的是:仅当进程 CPU 负载非常高的时候去抓取得到的 CPU Profile 才能真正反馈给我们问题所在。

在这个源自真实生产的案例中,我们也可以看到,正确和不正确地去使用 Node.js 开发应用其前后运行效率能达到二十倍的差距,Node.js 作为一门服务端技术栈发展至今日,其本身能够提供的性能是毋庸置疑的,绝大部分情况下执行效率不佳是由我们自身的业务代码或者三方库本身的 Bug 引起的,Node.js 性能平台 则可以帮助我们以比较方便的方式找出这些 Bug。


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

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

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

相关文章

python idea控制台中文乱码_解决IntelliJ IDEA 控制台输出中文乱码问题(史上最简单)...

解决IntelliJ IDEA 控制台输出中文乱码问题(史上最简单)首先&#xff0c;找到 IntelliJ IDEA 的安装目录&#xff0c;进入bin目录下&#xff0c;定位到idea.vmoptions文件&#xff0c;如下图所示&#xff1a;双击打开idea.vmoptions文件&#xff0c;如下图所示&#xff1a;然后…

VS Code 下载

官网地址&#xff1a; https://code.visualstudio.com/

通过DataWorks数据集成归档日志服务数据至MaxCompute进行离线分析

但是会遇到大家在分区上或者DataWorks调度参数配置问题&#xff0c;具体拿到真实的case模拟如下&#xff1a; 创建数据源&#xff1a; 步骤1 进入数据集成&#xff0c;点击作业数据源&#xff0c;进入Tab页面。 步骤2 点击右上角 新增数据源&#xff0c;选择消息队列…

这是我见过最卡通的 Python 算法了,通俗易懂

戳蓝字“CSDN云计算”关注我们哦&#xff01;普通程序员&#xff0c;不学算法&#xff0c;也可以成为大神吗&#xff1f;对不起&#xff0c;这个&#xff0c;绝对不可以。可是算法好难啊~~看两页书就想睡觉……所以就不学了吗&#xff1f;就一直当普通程序员吗&#xff1f;如果…

在 IntelliJ IDEA 中部署应用到服务器

在之前的文章《在 Intellij IDEA 中部署 Java 应用到 阿里云 ECS》中讲解了如何将一个本地应用部署到阿里云 ECS 上去&#xff0c;有些读者反馈目前还有一些测试机器是在经典网络&#xff0c;甚至是在本地机房中&#xff0c;咨询是否可以通过 Cloud Toolkit 插件将应用部署到这…

对话行癫:解密阿里云顶层设计和底层逻辑

几十个问题&#xff0c;万字长文&#xff0c;阿里云新任总裁行癫履新后首次出面与钛媒体独家深入讨论了一下阿里云对云计算未来的判断&#xff0c;深度解读未来阿里云生态战略&#xff0c;揭秘阿里技术委员会和阿里中台思想的原生思考。 阿里云智能总裁张建锋 钛媒体注&#x…

Python获取2019-nCoV疫情实时追踪数据

戳蓝字“CSDN云计算”关注我们哦&#xff01;来源 | 三行科创责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09; 2019-nCoV新型冠状病毒引发的肺炎牵动全国人民的心&#xff0c;无数无畏的英雄儿女逆行而上奔赴前线&#xff0c;作者也于2020年2月…

idea菜单栏消失如何调整回来

有一天, 把idea中 view下面的Appearance里面的Main Menu 给反选了 导致idea顶部的菜单栏给消失了, 就像下图这样 可以双击shift键&#xff0c;输入view&#xff0c;选择action里面的View 选择Appearance里面的Main Menu 即可把菜单栏调回来.

这款神秘的移动端OCR引擎,如何做到“所见即所得”?

阿里妹导读&#xff1a;随着深度学习&#xff0c;尤其是CNN和RNN等技术的飞速发展&#xff0c;文字识别技术(OCR)近几年得到了迅速的提升。与此同时&#xff0c;在智能化终端的大趋势下&#xff0c;本地化智能识别凭借更高效快捷的体验以及高度的隐私保护和零流量消耗等优势备受…

想要AI优先?数据优先才行

戳蓝字“CSDN云计算”关注我们哦&#xff01;来源 | forbes编译 | shawn责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09; 开展人工智能和机器学习项目的人很早就知道&#xff0c;机器学习项目不是应用程序开发项目。机器学习项目的大部分价值在…

Cloud Toolkit 部署应用到阿里云轻量应用服务器

在之前的文章《在 Intellij IDEA 中部署 Java 应用到 阿里云 ECS》中讲解了如何将一个本地应用部署到阿里云 ECS 上去&#xff0c;有些读者反馈目前正在使用阿里云轻量应用服务器&#xff0c;咨询是否可以通过 Cloud Toolkit 插件将应用部署到这些服务器上去&#xff1f;最新版…

springboot 排除 默认的loggback 和slf4j的依赖

文章目录异常现象&#xff1a;解决方案&#xff1a;总结异常现象&#xff1a; SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/D:/Program%20Files/JavaEclipse/repo/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.…

Node.js 应用故障排查手册 —— Node.js 性能平台使用指南

楔子 前一节中我们借助于 Chrome devtools 实现了对线上 Node.js 应用的 CPU/Memory 问题的排查定位&#xff0c;但是在实际生产实践中&#xff0c;大家会发现 Chrome devtools 更加偏向本地开发模式&#xff0c;因为显然 Chrome devtools 不会负责去生成分析问题所需要的 Dum…

懂编译真的可以为所欲为|不同前端框架下的代码转换

背景 整个前端领域在这几年迅速发展&#xff0c;前端框架也在不断变化&#xff0c;各团队选择的解决方案都不太一致&#xff0c;此外像小程序这种跨端场景和以往的研发方式也不太一样。在日常开发中往往会因为投放平台的不一样需要进行重新编码。前段时间我们需要在淘宝页面上…

面试稳了!网易资深工程师揭秘运维面经!

作者 | 阿文责编 | 伍杏玲出品 | 程序人生&#xff08;ID&#xff1a;coder_life&#xff09;受新型冠状病毒影响&#xff0c;很多企业都推迟了复工时间或集体开始远程办公&#xff0c;而一些企业本来计划春节过后开始春季招聘&#xff0c;但是受疫情影响已做出了调整&#xff…

java.lang.NumberFormatException: null

public static void main(String[] args) {String str null;try {int a Integer.parseInt(str);} catch (NumberFormatException e) {e.printStackTrace();}}

Pick!闲鱼亿级商品库中的秒级实时选品

一、业务背景 在电商运营工作中&#xff0c;营销活动是非常重要的部分&#xff0c;对用户增长和GMV都有很大帮助。对电商运营来说&#xff0c;如何从庞大的商品库中筛选出卖家优质商品并推送给有需要的买家购买是每时每刻都要思索的问题&#xff0c;而且这个过程需要尽可能快和…

一文看懂Microsoft Azure的十年变迁

来源 | forbes编译 | 火火酱责编 | Carol出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;当微软前首席软件架构师雷奥兹&#xff08;Ray Ozzie&#xff09;在2008年的PDC大会上发布Windows Azure时&#xff0c;没人能预估这个软件平台将会为该公司和整个行业…

74HC595

脚位图及说明 管脚说明&#xff1a; 14脚&#xff1a;DS&#xff08;SER&#xff09;&#xff0c;串行数据输入引脚 13脚&#xff1a;OE&#xff0c;输出使能控制脚&#xff0c;它是低电才使能输出&#xff0c;所以接GND 12脚&#xff1a;RCK&#xff08;STCP&#xff09;&…

UI2Code智能生成Flutter代码——机器生成代码

背景 在《UI2CODE--整体设计》篇中&#xff0c;我们提到UI2Code工程的整体流程。前步图片分析之后&#xff0c;我们可以得到对应的DSL布局描述。利用DSL的资讯&#xff0c;结合IntelliJ Plugin介面工具&#xff0c;面向使用者提供生成对应Flutter代码。 本篇主要介绍我们如何…