Wedge在实践一中,我们看到这两个表象都是CPU相关的生产问题,和我们在网上可能遇到的问题基本一致。是一类问题的典型案例,但其实这两个案例也有一个共同点:我们可以利用Node.js性能平台导出进程对应的CPUProfile信息来分析定位问题,但在网上一些极端情况下,我们遇到的故障是无法通过轻量级V8引擎暴露的CPUProfile接口获取足够的进程状态信息进行分析(仅部分定制的AliNode运行时版本支持,见下文)。处于无助的状态。本章将向您展示如何在Node.js的生产环境中处理此类相对极端的应用程序故障。本书首发于Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区同步更新。最小化重复代码的例子有点特殊。我们首先给出生产案例的最小化重现代码。感兴趣的同学可以自行运行。这样,再结合下面此类问题的排查过程,大家就看得更清楚了。当我们遇到这样的问题时,基于Egg.js,排错的最小代码如下:'usestrict';constController=require('egg').Controller;classRegexpControllerextendsController{asynclong(){const{ctx}=这个;//str模拟用户输入的问题字符串letstr='
'+'早餐后自由活动,指定时间集合自行退房。';str+='
'+'
'+'
';str+='
'+''+''+'
';str+='
';str+=''+''+'根据船期,自行前往暹粒机场,返回中国。
';str+='如需送货服务,需加收280/单。
';constr=str.replace(/(^(\s*?
\*?)+|(\s*?
\s*?)+?$)/igm,'');ctx.body=r;}}module.exports=RegexpController;问题应用状态其实这个例子可能对应很多问题场景Node.js开发都遇到过,很有意思,我们先来看看发生这种故障时我们的Node.js应用的状态。当我们收到平台配置的CPU告警信息后,登录性能平台进入对应的告警应用,找到CPU问题非常高的进程:然后点击数据趋势按钮,查看该进程的当前状态信息:可以看到进程的CPU利用率曲线一直处于接近100%的状态。这个时候进程就不再响应剩下的请求了,我们通过跳板机进入生产环境的时候可以看到进程其实还活着,并没有挂掉。这个时候我们基本可以判断:Node.js进程阻塞是因为它正在执行一个同步函数,一直卡在这个同步函数的执行中。Node.js的设计运行方式是单主线程,并发依赖底层实现的一组异步I/O和事件循环调度。简单的说,具体到事件循环中的某个时间点,如果我们正在执行一个耗时较长的同步函数(比如while循环耗时较长的循环退出),那么整个事件循环将是堵在这里,等它结束。往下看,这也是为什么不建议在非初始化逻辑中使用fs.readFileSync等同步方式的原因。排查方法这类问题其实很难排查,因为我们无从知道是何种用户输入导致了这种阻??塞,所以在本地重现问题几乎是不可能的。好在性能平台目前处理这种死循环问题的解决方案不止一种,下面我们来详细了解一下。一、CPUProfile这种分析方法可以说是我们的老朋友了,因为死循环的问题本质上是CPU高的问题,所以我们只需要抓取问题进程的CPUProfile,就可以看到是哪个函数了目前卡住了。需要注意的是,V8引擎提供的抓取CPUProfile文件的接口在进程假死时不能直接使用,所以在正确打开Chromedevtools一节中提到的v8-profiler等第三方模块无法使用工具章节。工作正常。不过定制的AliNoderuntime采取了一定的方法来避免这个问题,但遗憾的是,并不是所有的AliNoderuntime版本都支持死循环状态下的CPUProfile抓取。其实这里大家使用的runtime版本是要求:AliNodeV3版本需要>=v3.11.4AliNodeV4版本需要>=v4.2.1AliNodeV1和V2版本不支持如果你线上的AliNoderuntime版本刚好满足要求,可以按照之前Node.js性能平台指南中提到的,抓取问题进程的3分钟CPUProfile,使用AliNode定制的火焰图分析:这里可以看到3分钟抓到的问题进程的CPU全部被long函数中的replace方法消耗了,其实这和我们提供的最小化复现代码是一致的,所以可以判断是long函数中的regularization有问题,修复.二。诊断报告诊断报告也是AliNode定制的一种能力,可以导出更详细的Node.js进程当前状态。导出的信息还包括当前的JavaScript代码执行堆栈和一些其他进程和系统信息。它和CPUProfile的区别主要在两个地方:诊断报告主要是针对当前进程状态的导出,而CPUProfile是一段时间内JavaScript代码执行状态的诊断报告。除了此刻的JavaScript调用栈信息,还包括NativeC/C++Stack信息,Libuv句柄和一些操作系统信息当我们的进程处于假死状态时,很明显是不是一段时间或者此时的JavaScript执行状态,肯定是卡在了我们代码中的某个函数上,所以我们可以使用诊断报告来处理这样的问题。当然诊断报告功能对AliNode运行时版本也有要求:AliNodeV2版本需要>=v2.5.2AliNodeV3版本需要>=v3.11.8AliNodeV4版本需要>=v4。3.0AliNodeV1版本不支持,需要:Agenthub/Egg-alinode依赖Commandx版本>??=v1.5.3如果您使用的AliNoderuntime版本满足要求,您可以进入平台应用对应的实例信息页面,选择问题进程:并点击诊断报告可以生成问题进程此刻的状态信息报告:虽然诊断报告包含了很多进程和系统信息,但是属于比较轻量级的操作,所以很快就会结束。此时继续点击Dump按钮将生成的诊断报告上传到云端进行在线分析展示:继续点击Analysis按钮查看AliNode自定义的分析功能,展示结果如下:概览信息结果页面上比较简单,我们看JavaScript堆栈页面的内容,这里很明显也告诉我们当前JS函数卡在了long方法中,比CPUProfile更详细的是它还显示在long方法中哪一行被阻塞。对比我们提供给大家的最小复现代码,其实是在执行str.replace这一行,是题目正则匹配操作所在的地方。三、Coredump分析其实很多朋友看到这里都会有疑惑:既然CPUProfile分析和诊断报告已经可以发现问题,为什么还要继续推出比较重的coredump分析功能呢?其实原因很简单。无论是死循环状态下的CPUProfile抓取,还是使用诊断上报功能,都对问题进程的AliNode运行时版本有要求,更重要的是,我们只需要使用这两种方式即可。我们可以得到问题正则化的代码位置,但是我们无法知道在执行这样的正则化时,什么样的用户输入会触发进程阻塞问题,给我们分析和针对性治疗带来麻烦。所以这里最后介绍一下coredump分析功能,不需要任何AliNoderuntime版本,可以获得更准确的信息。首先,按照预备篇coredump部分提到的手动生成Coredump文件的方法,我们通过sudogcore
根据船期,自行前往暹粒机场返回中国。
如果需要送机服务,需要加280/perorder.
”,有了这个触发正则执行异常的问题字符串,我们有重要的信息可以依赖我们是否构造本地复现样本或进一步分析问题分析上一节我们使用了Node.js性能平台提供的三种不同的方法来分析定位线上应用出现假死状态的原因,这里给出一个简单解释一下为什么正则匹配字符串会导致类死循环,实际上是因为用户输入异常引发了正则表达式的灾难性回溯,导致执行时间长达数年甚至数十年。显然,无论哪种情况,单-mainworker线程模型会导致我们的Node.js应用处于假死状态,即进程还活着,只是不再处理新的请求。对经常回溯原因感兴趣的同学可以参考文章注意不要落入经常回溯的陷阱。归根结底,其实正则回溯导致的进程级阻塞问题,本质上是用户输入不可控导致的,Node.请求,所以更容易触发这样的问题。类似的问题在一些代码逻辑中其实也存在,比如while循环的退出条件,在某些情况下会失败,导致Node.js应用程序卡在循环中。以往即使知道进程被阻塞,也很难定位到具体的问题代码和导致问题的输入。现在,借助Node.js性能平台提供的coredump分析能力,相信大家可以相对轻松地解决此类问题。.本文作者:易君阅读原文,为云栖社区原创内容,未经允许不得转载。
