Wedge上一节我们使用Chromedevtools排查和定位在线Node.js应用中的CPU/Memory问题,但在实际生产实践中,你会发现Chromedevtools更偏向本地开发模式,因为显然Chromedevtools不会负责生成分析问题所需的dump文件,这意味着开发者必须额外设置v8-profiler和heapdump等工具,并通过附加实施的服务,可以实时导出在线运行的项目状态。另外,除了预备篇中的CPU/Memory问题,我们还会遇到一些需要分析错误日志、磁盘和coredump文件来定位问题的情况。因此,在这些场景中,Chromedevtools显然可以解决问题。有一些力不从心。正是为了解决广大Node.js开发者的这些痛点,我们推荐大家使用Node.js性能平台,原AliNode,它已经承载了阿里巴巴集团内几乎所有的Node.js应用上线。运行监控排查,让您放心在生产环境中部署使用。本节将从Node.js性能平台的设计架构、核心能力和最佳实践等角度,帮助开发者更好地使用该工具解决上述异常指标分析和在线Node.js应用故障定位。本书首发于Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区同步更新。架构Node.js性能平台实际上由三部分组成:云控制台+AliNode运行时+Agenthub,如下图所示:具体部署步骤请参考官方文档:运行时自助部署。借助Node.js性能平台的全套解决方案,我们可以轻松实现预备篇中提到的大部分异常指标的告警分析能力。在生产实践过程中,在我看来,Node.js性能平台方案其实只提供了三个最核心也是最有效的能力:异常指标告警,即当一些异常指标在准备环节出现异常时它可以通过短信/钉钉通知导出在线Node.js应用状态给开发者,包括但不限于之前Chromedevtools部分提到的CPU/Memory状态,导出在线分析结果和更好的UI展示,自定义分析应用导出状态和display更符合国内开发者的习惯。也就是说,Node.js性能平台作为一个产品本身,也在不断的迭代添加和修改,但是必须把保证以上三个核心能力作为第一要务。其他角角落落的功能响应优先级相对没有那么高。其实我们也明白,使用该平台的开发者希望能在一个地方看到Node.js在线应用从底层到业务层的所有细节。不过个人觉得不同的工具应该有自己的核心能力输出。很多时候,不断的添加很容易让产品本身的定位变得模糊、笼统而不精准。节点。.js应用的开发者再也不能在面对一些低级的线上疑难杂症时手足无措了。BestPracticeI.配置合适的告警在线应用告警实际上是一种自我发现问题的保护机制。如果没有告警能力,直到问题暴露给用户侧并引起其反馈后,问题才会被发现。这是显而易见的。对用户体验非常不友好。因此,开发者在部署项目后,首先需要配置相应的告警。在我们的生产实践中,通过错误日志、Node.js进程的CPU/Memory分析、核心转储(Coredump)分析和磁盘分析来检测线上问题,因此我们需要的基本告警策略也来源于以上五个部分。幸运的是,平台已经为我们预设了这些警报。这里只需要选择一个即可完成告警配置,如下图:在Node.js性能平台的告警页面,有一个快速添加规则,点击选择Afterwards,阈值表达式模板和告警告警规则的描述模板会自动生成,我们可以根据项目的实际监控需求进行修改。例如,如果要监控Node.js进程的堆内存,可以选择Memorywarning选项,如下图:此时点击AddAlarmItem,完成对进程堆内存的告警,而当有报警发生时,需要点击NotificationSettings->AddtoContactList将联系人添加到这条规则中,如下图:那么在这个默认规则的例子中,当我们Node分配的堆内存.js进程超过在线堆的80%(64位机器上默认的堆限制为1.4G),它会触发短信通知到该规则绑定到此联系人的配置。事实上,快速添加规则列表中提供了最常见的预配置报警策略。如果这些不能满足你的需求,你可以参考官方文档Alarm了解更多自定义服务告警策略配置方法设置。除了短信报警,还支持钉钉机器人向群推送报警信息,方便群发感知在线Node.js应用状态。二。根据告警类型进行分析按照上一节的配置配置好相应的告警规则后,您就可以在收到告警信息时,根据策略的类型进行相应的分析。本节将根据预科中比较常见的五类题型进行一一讲解。A。磁盘监控是一个相对容易处理的问题。在快加规则中,我们实际上会在服务器磁盘使用率超过85%时发出告警。收到磁盘告警后,可以连接服务器,使用如下命令查看那个目录占用很大:sudodu-h--max-depth=1/找到占比比较高的目录和文件后,查看是否需要备份和删除以释放磁盘空间。b.错误日志收到具体的错误日志告警后,只需要到对应项目的Node.js性能平台控制台找到问题实例即可查看其异常日志,如下图:这里会整理根据错误类型,您可以通过显示的错误堆栈信息来定位相应的问题。注意这里的错误日志文件需要你在部署Agenthub时写配置文件。具体配置请参考文档配置和启动Agenthub部分的详细配置。C。进程的高CPU终于达到了上一节v8-profiler导出然后Chromedevtools分析的异常类型。那么在Node.js性能平台的整体解决方案下,我们不需要依赖v8-profiler这样的第三方库来导出进程状态。相比之下,当我们收到Node.js应用进程的CPU超过我们设置的阈值告警时,我们只需要点击控制台对应实例上的CPUProfile按钮即可:默认是一个3分钟的CPUProfile文件会为抓取的进程生成生成文件,结束后会显示生成的文件在File页面:此时点击Dump上传到云端进行在线分析展示,如下图:这里可以看到那里面有两个Analysis按钮,其实第二个下标有Analysisbuttonwith(devtools)其实就是上一节说的Chromedevtools分析。我不会在这里重复解释。如果忘记了,可以复习一下本章前面的内容。下面重点分析第一个AliNode定制。点击第一个分析按钮后,可以在新页面看到如下内容:这其实是一个火焰图,但是它和Chromedevtools提供的火焰图不同的是,这里是将抓取到的数据聚合显示出来的火焰图3分钟内执行JS函数。在一些同一个函数被执行多次的情况下(可能每次执行的时间都很短),聚合后的火焰图可以很方便的帮我们找到代码的执行瓶颈进行相应的优化。值得一提的是,如果你使用的AliNode运行时版本在v3.11.4或v4.2.1以上(包括这两个版本),当你的应用出现死循环问题时,比如regularBacktracking(即需要超过十年执行此规则,类似于Node.js进程中的死循环),抓取CPUProfile文件即可轻松定位到问题代码。对细节感兴趣的同学可以看到Node.js性能平台支持死循环和正则攻击定位。d.内存泄漏与高CPU问题相同。当我们收到Node.js应用进程的堆内存与堆限制的比例超过我们设置的阈值时,我们不需要像heapdump这样的第三方模块来导出堆快照进行分析,我们点击Heap控制台对应实例上的Snapshot按钮生成Node.js进程对应的堆快照:生成的堆快照文件也会显示在文件列表页面,点击Dump将堆快照上传到云端进行进一步分析接下来的分析:同上,带下标的分析按钮(devtools)还是上一节说的Chromedevtools分析。这里重点分析AliNode自定义的第一个分析按钮。点击后会出现一个新的页面如下图:首先解释一下上面overview栏中的内容信息:Filesize:堆快照文件本身的大小ShallowSzietotalsize:回头看里面的内容上一节,GCroot的RetainedSize其实就是heapsize。也等于堆上分配的所有对象的总ShallowSize,所以这里其实是使用的堆空间对象数:TotalnumberofHeapObjectscurrentlyallocatedontheheapNumberofobjectsides:这个比较抽象一点,如果ObjectA.b指向另一个ObjectB,我们认为表示指向关系的b属性是一侧的GCRoots的数量:V8引擎实现的堆分配实际上并不是只有一个GCRoot的情况我们简化了以帮助您理解。的运行模型下,堆空间有很多GC根。这是GC根的真实数量。这部分信息旨在为您提供一个概览。需要深入阅读一些信息以了解堆快照。如果实在看不懂有些overview指标信息其实是无害的,因为不影响我们定位问题代码。在简单了解了概览信息的含义之后,我们来看一下对于定位Node.js应用代码段非常重要的信息。首先是默认显示的可疑点信息。上图中的内容表明@15249这个对象占据了heap97.41%的内存空间,那么它很可能是一个泄漏对象。这里有两种可能:对象本身应该释放,自己却没有释放,导致堆空间占用这么大。对象的一些属性本该释放却没有释放,造成这个对象占用大量堆空间的现象。判断是哪种情况,跟踪对应的代码段,我们需要点击图中的ClusterView链接进一步观察:这里继续说明什么是clusterview,clusterview其实就是支配树的别名,即也就是说,我们在这个视图中看到的是从上一节提到的疑似泄漏对象开始的支配树视图。它的好处是,在这个视图中,父节点的RetainedSize可以直接通过添加其子节点的RetainedSize,然后添加父节点本身的ShallowSize来获得。节点被占用。并且结合上一节对支配树的描述,我们可以知道,支配树下的父子节点关系不一定是堆上真实空间中对象的父子关系,但是对于那些父子节点-真实堆空间中支配树下的子关系也存在父子节点关系的簇节点。我们还用浅紫色标记了真正的边缘。这部分边缘信息对我们映射到真正的代码段很有帮助。在这个简单的例子中,我们可以清楚的看到,疑似泄露对象@15249其实是由下属的test-alinode.js中的一个数组变量引起的,该变量存储了4个45.78兆的数组,这样如果找到问题代码,后续可以进行优化。在实际生产环境的堆快照分析下,很多情况下,集群视图中的父子关系在真实的堆空间中是不存在的,所以不会有这些紫边信息的展示。这时候,我们想知道可疑的泄露对象是如何通过JavaScript生成的对象间引用关系来引用实际占用堆空间的对象的(比如上图中的40兆Array对象),我们可以点击可疑节点本身的地址链接:这会进入这个以对象为起点的堆空间中真实的对象引用关系视图搜索视图:因为这个视图反映了堆空间中各个HeapObject之间真实的引用连接关系,父对象的RetainedSize不能直接由子节点的RetainedSize决定。累计获取,如上图红框所示,很明显这里三个子节点的累计RetainedSize已经超过了100%,这也是Search视图和cluster视图的一个比较大的区别。在Search视图的帮助下,我们可以根据其中反映的对象和边缘之间的关系,定位到我们的JavaScript代码中生成疑似泄漏对象的特定部分。其实看到这里,有些读者应该明白了,这里的Search视图其实对应的是上一节提到的Chromedevtools的Containment视图,只不过这里的起点是我们选择的object本身。最后需要提一下Retainers视图,它和上一节提到的Chromedevtools解析堆快照显示结果中的Retainers同义。它表示对象的父引用关系链。我们看一下:这里globa@1279对象的clearImmediate属性指向timers.js()@15325,而timers.js()@15325的context属性指向疑似泄露对象system/Context@15249。大多数情况下,通过结合Search视图和Retainers视图,我们可以定位到指定对象在JavaScript代码中生成的位置,而在Cluster视图中,我们可以轻松知道哪些对象占用了堆空间,然后结合这两部分的信息,我们就可以分析在线内存泄漏的问题,定位代码。e.当发生coredump时,最后就是收到服务器生成coredump文件(Coredumpfile)的告警,也就是说我们的进程发生了意外的Crash。如果你的Agenthub配置正常,在文件->Coredump文件页面会自动显示生成的coredump文件信息:和前面的步骤类似,我们要看服务端的分析和结果展示,首先我们需要dump将服务器上生成的coredump文件上传到云端,但与之前的CPUProfile和heapsnapshotdump不同的是,对coredump文件的解析需要我们提供Node.js对应的启动执行文件。Runtimeversion:点击Setruntimeversion进行设置,格式为alinode-v{x}.{y}.{z},比如alinode-v3.11.5,会验证版本,请务必填写你的应用实际使用的AliNode运行时版本。版本填好后,我们可以点击Dump按钮将文件转储到云端:很明显,对于coredump文件,Chromedevtools没有提供分析功能,所以只有AliNode自定义的分析按钮,点击Analyze按钮查看结果:此处第一栏的概览信息,阅读文字说明即可理解,此处不再赘述。我们来看看比较重要的默认视图BackTrace信息视图,这个视图中显示的其实是Node.js应用在Crash时的线程信息。许多开发人员认为Node.js是一种单线程运行模型。其实这句话也不是完全错误的。更准确的说法是单主JavaScriptWorker线程,因为实际上Node.js也会开启一些后台线程来处理一些任务,比如GC。大多数情况下,应用程序的崩溃是由JavaScript工作线程引起的,所以我们只需要关注这个线程即可。在这里,很明显JavaScript工作线程在BackTrace信息视图中被标记为红色并位于顶部。展开后可以看到Node.js应用Crash瞬间的错误堆栈信息:因为即使在JavaScript工作线程中,也会有NativeC/C++代码的渗透,但是在排查中我们往往只需要看到一样的红色flagJavaScript堆栈信息就足够了,在这个简单的例子中,显然Crash是由视图启动一个不存在的JS文件引起的。值得一提的是,coredump文件的解析功能是非常强大的,因为在前言中我们提到了在Node.js时,它的生成方式不仅仅受系统内核控制。该命令是手动强制输出的,而这一节我们看到分析coredump文件其实可以看到此刻的JavaScript栈信息及其输入参数。结合这两点,我们就可以出现在CPUProfile部分提到的线上了。在处理死循环问题时,直接使用gcore生成一个coredump文件,然后上传到平台云端进行分析。这样一来,我们不仅可以看到自己的Node.js应用中是哪一行JavaScript代码被阻塞了,而且导致阻塞的参数也可以完整的获取到,这对于局部复现和定位问题无疑是有很大帮助的。最后,本节实际介绍了Node.js性能平台一整套针对Node.js应用开发的监控、告警、分析、问题定位解决方案的架构和最佳实践。最好结合自己的项目使用,有一个整体的认识。限于篇幅,最佳实践中CPUProfile、heapsnapshot和coredumpfile的分析示例非常简单,这部分的内容更多是为了帮助大家了解平台提供的工具的使用方法及其分析结果在本书的第三节中,我们将通过Node.js性能平台提供的上述工具的分析过程,通过一些实际生产中遇到的案例问题,帮助大家更好地理解这部分信息,也希望大家看完这些内容可以有所收获,对在生产中使用Node.js应用更有信心。本文作者:易君阅读原文,为云栖社区原创内容,未经允许不得转载。
