Serverless下的NodeJS Runtime监控及Profile

注:文章整理自腾讯云高级前端工程师陈家兴在Hello Serverless 沙龙深圳站上的演讲,演讲主题为《NodeJS Runtime监控》,感兴趣的读者可关注公众号,后台回复「Serverless 深圳」领取讲师演讲PDF。

根据统计数据,SCF的用户中,NodeJs和Python的用户是最多的,而相信在座的各位应该有很多就是NodeJS的开发者,大家对监控方面有过实践或者感兴趣的话应该能有自己的收获,而如果你不是Node的开发者,那也没关系,其中的很多原理都是相通的,也希望各位能从不同的角度看这个话题,应该能碰撞出更多火花。我这次分享的主题是node JS runtime监控,我这里就先花一点点时间说两句为啥要做监控。

监控的作用

相信在座的各位都是有相当开发经验的开发者了,我们日常是否需要监控?肯定需要!但实际监控都能做到什么,可能有些同学其实并没有太明确的概念。

实际上,监控的作用很简单,只有两个,第一个就是发现问题,监控一般跟数据跟图表挂钩,当数据和图表表现出跟往常不一样的特征的时候,我们马上就能知道,可能是哪里出问题。

而另一个作用就是解决问题,一般来说,不同的问题会呈现不一样的数据特征,比如说,内存呈不断上升的,到了顶峰突然又断崖式地降下来,明显就是内存泄露到最后内存耗尽,最后服务自动重启的特征,通过数据的不同特征,就可以根据经验或者推理,找到问题的原因,进而验证和解决问题。

简单讲完为什么需要监控,就来讲这次的分享的重点,Node Runtiem级监控。

我们先来看看常规的监控,常规的都能监控到什么呢。

  • CPU使用率 —— CPU使用的百分比
  • 内存使用量
  • 出包入包量和网卡流量 —— 互联网基本上所有应用都会跟网络沟通

而runtime级别的监控都能监控到什么呢,CPU使用时间,其中包括系统时间和用户时间,Node程序内内存使用情况,里面包了程序内存消耗总量,实际内存使用量

,空闲内存量,等等。下面还有一个Event loop Lag,我后面再详细说一下。

很明显的对比就是,常规监控都是一个概览额总值,而Runtime级别的监控下是更详细的数据,包括内部使用上面的各方面的细节,而更详细的数据,对开发者无疑就意味着更容易发现问题和解决问题。

大文豪鲁迅曾经说过,Talk is cheap, show me the code。这里先看看API,对于CPU来说,有两个API可以获取到相关的信息,一方面是cpuUsage,另一方面是cpus。

明明都是CPU信息,为什么会在两个不同的库下面呢,其实理解里面的内容就会发现挺有道理的,后者在os库下面,给出的其实是系统CPU的信息,前者放在process库中,是当前进程使用CPU的信息。所以,从runtime级别监控的观点来看,后者其实不是rumtime级别需要关注的,我们需要关注的是前者,前者返回的是类似{ user: 65655, system: 24929 }的结构。

对于内存来说,这里也是主要有两个API可以获取内存相关的信息,先是memoryUsage接口,能获取到。

  • rss:node进程总内存占用量
  • heapTotal:总堆内存占用量(已申请下来的)
  • heapUsed:实际堆内存使用量
  • external:扩展等外部程序的内存占用量

而getHeapStatistics获取到的信息跟memoryUsage是有重合的,这里就不详细介绍了,就说两个点,一个是getHeapStatistics能获取到最大可用堆内存,这是memoryUsage没有返回的,至于node可用的最大内存,比较久之前的版本会有即使调整—max-old-space-size也只能到1.7G(64bit)的内存,目前使用的node的版本也都去掉了这个限制。

另一个是,在node10的getHeapStatistics 返回的数据多了两个值。number_of_native_contexts native_context 的值是当前活动的顶层上下文的数量。 随着时间的推移,此数字的增加表示内存泄漏。number_of_detached_contexts detached_context 的值是已分离但尚未回收垃圾的上下文数。 该数字不为零表示潜在的内存泄漏。

关于Event loop lag,Event loop lag直译就是事件循环延迟,我觉得可能叫异步调用延迟会比较合适。

这里我先放一张阮一峰老师用过的@busyrich的一张图,这张图说的是NodeJS的事件循环是怎样运作的,众所周知,NodeJS是单线程的,异步任务的调度在nodeJS的环境下是由LibUV库运作的,我也不再这里长篇大论地解释Event loop了,如果对此还不太清楚的同学可以到阮一峰老师的博客学习一下。这里只说一下这个延时到底是怎么来的,简单点来说,我们设定一个异步任务,在同步队列执行完之后就是马上执行,但是如果同步队列一直被阻塞的话,就是出现异步任务延时执行的现象,这种现象在一些CPU密集型的服务中会比较常见,如果你的服务的异步任务执行延时忽然不正常了,很可能就在某个地方出现了类似死循环的问题,同步任务把队列占满了,当然,死循环往往也会伴随内存泄露出现。

Event loop lag没有API能直接获取,不过测量起来也非常简单,这里放上简单的原理代码。直接利用setTimeout的实际执行时间和设定时间直接的差值,这份代码是我随手撸的用以说明原理的demo,实际使用上还要稍作加工,npm也有库能直接使用,也写得非常简单实用。

监控性能消耗

Runtime级别监控对比外部监控还有一个不一样,就是需要介入到Runtime中,不难想象,做数据的收集肯定是会对性能有一些影响的,可能我们就会担心会不会大幅影响性能呢,为此我特意在云函数上做了一些测试。

在耗时上,在一个比较简单的服务上,添加监控后发现,耗时的确是高了蛮多,但随着测试样本增大,平均耗时有一直下降的趋势,说明添加监控初始化时可能会消耗大概几十ms左右的时间,后面其实每次增加的耗时也就不到1毫秒。

在内存消耗上这种趋势会更加明显一些,需要一点点内存来缓存数据,次数一多起来,差别基本就趋于0了,所以综上所述,rumtime里面添加监控的性能消耗其实都是非常小的。

Runtime Profile

很多时候,监控数据更多用于发现问题,有些更复杂的问题,还得需要更详细的信息,这里就涉及到做Runtime的profile了。

从现实的场景出发,我们往往就像这个狼叔一样,后面的服务器都炸了,可是我们还是一脸懵逼的状态,我们往往发现问题是源于接口调用成功率降低,或者调用时间不正常地变长了,那出现这些问题的原因其实可能有很多,有可能是出现了逻辑死循环,有可能是内存泄漏,频繁触发GC,node的GC其实是非常消耗系统资源的,甚至GC也解决不了问题,导致内存耗尽,需要重启服务,那么出现这些问题的时候除了监控数据,还有什么东西能帮助我排查到底是哪里的问题呢?

这里就需要profile出场了,我会主要介绍两种profile,一个是heap dump,也就是内存快照,另外就是CPU profile。

估计很多开发者都会接触过heap dump,一般遇到疑似内存泄漏问题,第一反应就是打快照,这里就是快照导入到chrome devtool后的效果。

V8-profiler(v8-profiler-node8)

Heapdump

这两个包都可以做内存的heapdump,原理都是一样的,具体用法大家可以到对应的包下面看看用法。

得出的包导入到chrome后可以查看里面的内容了,主要有这几种模式:

  • Summary
  • Comparison
  • Containment
  • Statistics

summary就是一个总览,可以看到不同constructor创建的对象占用内存的细节。

Comparison 是调试时候最常用的一栏,用于对比不同时间点的快照之间的异同,一般来说,在问题发生前,和问题发生后进行快照,然后对比两者之间新增了哪些内容,就比较容易找出问题的原因。

Containment 视图允许您探索堆内容。此视图提供了一种更好的对象结构视图,有助于分析全局命名空间 (window) 中引用的对象以找出是什么让它们始终如影随形。使用此视图可以分析闭包以及在较低级别深入了解您的对象。

Statistics 就是以饼图的形式给出不同内容的占比。

CPU profile可以通过查看不同函数的运行占用CPU时间,看出什么地方占用了大量CPU时间,这就就是看cpu profile的入口,在调试工具窗口的右上角更多栏打开,more tools,javascript profile栏下,嗯,没错,我之前也在吐槽,为啥这个玩意要藏这么深,其实是有原因的,chrome的devtool也是不断在进化,而这个js profile的功能其实已经很大程度上整合到了performance tab之下了,performance tab下面的除了jsprofile外,还会dom渲染,图片加载等页面性能的细节,可惜这些特性对node也是没用的,所以我们目前还是在用的原来的这个工具

同样,通过V8-profiler可以获取CPU profile。

同样有几个视图:

  • Tree 则是通过调用树的顺序,列出不同函数的消耗时间。
  • 这个就是charts视图下的时序火焰图,能更好地以时间为轴看到整个代码执行的过程。
  • Chart 通过时序火焰图的形式展示不同函数的时间占用。

能更好地以时间为轴看到整个代码执行的过程。

通过flamegraph 这个npm包也能根据CPU profile生成火焰图的SVG进而进行查看,通过火焰图,能非常直观地看出,到底是哪个进程消耗了大量的时间。

值得留意的是,这个火焰图跟前面说的charts视图下的时序火焰图是不一样的,这个火焰图会根据相同的函数进行归类,能比较直观的看出其中耗时最长的步骤

说了这么多,跟severless有啥关系呢,这些东西如何在SCF上实现呢?

云函数下的NodeJS Runtime监控

serverless的程序也需要做监控嘛,原理上面都讲了,收集的数据放到DB,profile放到cos,然后慢慢分析就好啦。

SCF一键开启吼不吼啊,在SCF上,性能分析都能自动化,惊不惊喜,意不意外,目前SCF上一键开启分析功能已经在开发中,具体会做些什么呢?

Serverless 虽然是不用关心服务器等基础设施,但是程序的性能还是要关注的,毕竟128m比256m便宜,更快的处理 & 响应速度也非常重要。

其次在云函数上,每次调用的确是相互独立的,可是容器和实例都是必须复用的,关于冷启动和热启动的话题也总是云函数的热门话题,如果函数频繁遇到冷启动,除了调度问题外,也有可能是代码本身的问题,比如函数调用多次后出现内存泄漏导致out of memory,也就是内存耗尽了,自然会触发重启,出现冷启动。

所以,根据云函数的特性,我们对收集的信息也会进行定制化,首先收集快照的时机会在函数执前跟执行后,这样就能很直观地看到函数执行过程中内存的变化,而cpu profile则会全过程收集,详细记录函数执行的全过程。

另外,我们还会输出函数调用过程中的GC log,以便进行进一步的收集。

总而言之,无论常规监控还是runtime级别的监控,都是帮助我们更好地把握程序的健康状况,对我们日常开发运维都非常重要。同时,经过测试,即使性能消耗比较高的runtime级别监控,其实其性能消耗也在控制范围以内,在不添加太多工作量的前提下,我们都应该尽量全方位地监控我们的程序。

另一方面,如何在发现除了问题的时候更好地解决问题,做Profile是最高效的方法。

而云函数,或者说Severless,都在尽己所能为所有的开发者服务,降低接入和分析门槛,提供最方便的监控和profile服务。

本文分享自微信公众号 - ServerlessCloudNative(ServerlessGo)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Rust语言学习交流

【Rust日报】 2019-08-09:我學習rust的前三個月

到目前為止,感謝社區的回饋與rust團隊對rust語言的維護,也感謝tokio有專業的團隊在維護。

12930
来自专栏腾讯开源的专栏

Hadoop Meetup 现场直播

? 自2006年诞生以来,Hadoop技术与社区已走过了13个年头,在经历了大数据技术高速发展之后,迎来了3.x时代。Hadoop如何在腾讯、阿里、滴滴、小米...

12640
来自专栏李才哥

Vue的安装及使用快速入门

注:安装过程 中有个选项(Use ESLint to line your code ?选择 No )

14140
来自专栏阿策小和尚

【Flutter 专题】14 基本的 http 网络请求学习下~

和尚搭建了几个基本的小页面,现在需要添加其中的业务逻辑,这就必不可少的用到网络请求;Flutter 中提供了 dart.io 方式进行网络请求,不管...

9130
来自专栏yl 成长笔记

vscode 配置 nodejs 开发环境

84920
来自专栏iOS小生活

Texture

Texture原名是AsyncDisplayKit,是Facebook的paper团队发布的一个基于UIKit的库,这个库能够将图片加载、布局计算以及UI渲染等...

23760
来自专栏数据猿

Hadoop YARN:调度性能优化实践

今日头条丨一点资讯丨腾讯丨搜狐丨网易丨凤凰丨阿里UC大鱼丨新浪微博丨新浪看点丨百度百家丨博客中国丨趣头条丨腾讯云·云+社区

10520
来自专栏前端技术分享|前沿资讯|读书分享

JavaScript执行——Promise

  Promise构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve和 reject。它们是两个函数,由JavaScript引擎提供,不用自己...

11520
来自专栏前端技术分享|前沿资讯|读书分享

js模拟交通信号灯

  首先,div元素块设置css变圆;其次,每隔一定时间,转换元素块背景色;接着,控制显示时长;然后,将上两步组合;最后,无限循环执行。

10930
来自专栏前端技术分享|前沿资讯|读书分享

Hello React

  至此,Mac环境下react基于脚手架的开发环境已搭建完成。运行项目( npm start)后,浏览器会自动打开本地页面http://localhost:3...

8310

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励