导语 背景是最近做了一个CSIG大讲堂的分享,总结和梳理了这两年多来在Nodejs 相关学习的知识和思考,关于“调试工具” 和 “Node Server 后台问题处理” 这一部分,还是相对比较有意思的。PPT内容有一些过多。所以在PPT中抽离出来,单独梳理了一篇文章,跟大家一起分享一下。知识都是前人的知识,我只是知识的学习者和搬运工。

前言 :

如果要对服务进行优化,就需要先测量服务的瓶颈。优化的前提是——测量。没有平白无故的性能提升,也没有拍脑袋的期望指标。只有通过数据参考下的衡量标准,来判断事物未来的发展,这样相对客观。当然,生活有时候也有喜从天降的事情发生,比如我的一个好朋友,今年居然喜当爹了。当然,我是不会告诉你他是谁的!这里只是举个例子而已。小概率的事情,一般不会发生在大部分人的身上。

1、如今,还在诟病 Nodejs 什么

在 Nodejs 的世界里,发生了什么,怎么发生的,人们都不需要担心,都可以找到原因。有些不了解 Nodejs 开发的同学,特别诟病 Nodejs 的问题有几个,我来描述下:

1、JS 异步回调地狱,写 JS 真垃圾,代码不好维护代码;

2、单线程是那么脆弱,crash 频发,它就是个玩具语言;

3、Nodejs 调试不方便,无法定位问题,查问题只靠蒙。

拜托,2018年了。那些问题,早已烟消云散了。

今天的 Nodejs 已经相对完善的解决了这些问题,行业里面已经有越来越多的团队和企业,在使用 Nodejs 来构建他们的企业级的后台服务了,作为其整个业务体系后台架构中的重要组成部分。列个 list 举例:

在英超曾经流传了一句话:“无论你爱或者不爱,弗格森就在哪里”。虽然爵爷退休了,但是作为曼联的球迷,不会忘记爵爷这些年的留下的辉煌的印记。放到现在呢,无论你是否对 Nodejs 曾经有过,或是现在依旧保有偏见,都无法阻止它成为行业内,后台服务的技术选型之一。

技术本身只有它的使用的场景不合适,而没有技术本身不合适一说。让合适的技术,做合适的事情,这是团队架构师的职责和本分,而行业趋势,不会因为某些人的喜欢,或者不喜欢,就停下技术革新的脚步。

2、APM,让子弹在飞一会儿

某种程度上讲,做好监控上报,是服务稳定最重要的环节。比如上面的这些性能指标。我们从左向右,从上到下的看。容器监控这一块,在腾讯内部大家可以接入 TNM2 即可,在这方面是公司的骨灰级玩家。出去外面的话,阿里云全套解决方案也是非常OK的。

进程监控这一块,大家可以自己写脚本上报,或者为了省事,直接对接 tars、或者 tsw 即可。特性上报 跟业务相关性比较大。数据上报可以接入 TDW、罗盘,或者任性的你,也可以造一个监控系统,开心就好。

日志染色这一块,TSW 目前做的界面是比较友好的。但个人认为,整个项目组从前到后,接入 ELK 就可以了。美不美这个意义不大。测试环境用我们团队同学做的 NoHost,前端同学用一下这个,聪明的你,就可以愉快的玩耍了。

至于 Node server 监控,Nodejs 一般都是做 Web 服务,像直出失败率、CGI的请求延时、成功率,都是比较常规的监控指标。找个地方报一下就可以了。比如 monitor。最后 V8 的 pref,有条件的团队,不妨就搞个灰度机器,每次上线的重要需求,或者大版本,可以先多灰度度一段时间看看,比如支付业务。比较麻烦的是有些业务如果上线之后,服务不能回滚,万一遇到问题,那开发就只能死扛到底,直到问题解决为止了。

有了监控之后,自然就会有告警,后面方便第一时间发现问题,告警不要搞太多,就跟短信不要钱一样,发那么多,会被烦死的,太多后面就麻木了,贵在于精。

这里没有描述进程管理这个纬度的事情,比如心跳检测、进程保活、僵尸进程检测这类问题,主要这些问题太过基础了,并且团队在封装基础框架的时候,都会处理掉这些基础问题,那部分代码经过长久的迭代和测试,都是相对非常稳定的。因此,在这里不作为服务端的监控指标了。当然,依旧可以作为上报的指标,多上报一些也是好的。

永远记得:监控是保证服务稳定性的最重要的手段!

3、Debug Nodejs in our life

我们解决一个问题的流程差不多是如下流程:先有个假设,去测试,收集和分析数据,然后得出结论,再去验证我们的假设是否正确,循环往复,直到有了结果,或者放弃对答案的寻找。

遇到了一个问题,如果方便的通过日志查看,就可以迅速定位和解决,那还是很容易修改的。但是有些问题就比较麻烦,比如内存泄漏这种。可能必须要去还原现场,才能找到问题的答案。这个时候,如何去还原现场,就是一个很繁琐的事了,而且不仅要还原,还必须要保存现场。并且要在运行时保存。

在做分享 PPT 的时候,收集了一些工具,介绍一些在什么场景,用那个比较好,其实非常多,这里分享一些相对比较典型的使用工具跟大家介绍一下。

1)开发调试

(1) inspect

功能上 日志、Profile、Sources、Memory 应用有尽有。推荐大家使用,毕竟 chrome 出品的工具,还是非常优质的。

(2)  process._debugProcess

“如果一个 Node.js 进程已经启动,没有添加 —inspect 参数,我们不想重启(会丢失现场)又想调试怎么办?

ps -ef | grep app.js 获取 pid

node -e "process._debugProcess(pid)"

前段时间,看了 Nodejs 这个关于调试部分源码,比较可惜的是,官方 9229 的端口写的是常量。本地改了一版本是可以把端口迁移到另一个的,比如 8080(备注:公司内网 8080是一个调试端口)。整个过程非常麻烦,C++代码要改的地方不少,虽然成功了,但不是很稳定。重要的事情 Node 版本在 6、8、10,在 debug 这部分的代码差异比较大。所以,用这个做线上调试,目前短期内通过自己修改源码,编译成 Node版本,还不是很方便。

(3) VS Code

其实,VS code 还是真的挺方便的。

服务远程调试,是非常低频的事情。有需要就自己搭建一下。

2)CPU

(1) 火焰图

火焰图还是一个比较方便定位问题的手段。它的水平方向是CPU的耗时,纵向是调用深度。可以不同的颜色代表了不同代码区域。C++、JS Native、优化编译代码、以及 JS 代码。有时间做服务优化的时候,会是一个比较方便的手段。如果应付一般的业务需求,意义不大,因为开发时间很紧急,功能都无法完成的时候,何谈优化。

而且,除非真的贫困的部门,或者代码写的真的惨不忍睹,或者在非常极端特别的情况下。否则,机器成本其实可以忽略不计的,多部署几台机器,扩容一下,很便宜的!有时间多搞点新业务,或者拓展一下产品的特性,随便一个产品火了,几万台服务器的钱都赚回来了。虽然都强调成本意识,但是能赚钱才是王道。

(2) 其他工具

v8-analytics、v8-profiler 、node —prof app.js

3)内存

先看一下内存泄漏的原因:全局变量、事件监听、闭包、setTimeout … 整理的工具有点多,自己也有点方。

推荐一款比较常用的 heapdump,以及 node-clinic 可以了解一下。mac 上面兼容度也非常不错。很大概率,我们写代码是遇不到这些问题的,除非要造一个这样的场景出来。尽量使用局部变量,用 let 声明,事件封装的时候,记得写个销毁的方法,自动去 clear ;定时器使用的时候,留心一下。假如真的遇到了问题,恭喜你中奖了。好好整理一下案例,有机会要分享给我。

4、总结 Node Server 后台常见问题和解决方案

大概率会是下面的这些问题,整理了一下,也许覆盖的不全,大家可以一起补充。

上面大致描述了一些出现问题引发的原因,感谢BJ之前的一次分享,我从C++移动到了 Nodejs 上面,不一定百分百命中。虽然海量服务这东西都被大家习以为常了,但是写过每天千万,或者亿级以上流量的后台开发同学,可能还是没有那么多。毕竟行业每天过千万UV的产品,也没有看起来那么多。所以,流量的大小是业务复杂度核心之一。

有时候我也在想一个问题,老是说 ToB 的业务复杂,但是复杂不等于技术能力。那么业务复杂是否能体现架构能力呢?看起来是可以的,但是我们如何评价架构能力的高低呢?灵活可扩展的业务设计能力,的确是一件很重要的事情,但更大的场景是:产品压力大,开发疲于奔命,很难有时间做好的架构设计。而且很多时候,还没体现出开发设计产品的架构能力时,这个产品形态的产品案例已经挂了,又如何验证真实的架构能力呢?

所以,架构本身这个命题就有点不好评价了。那么 ToC 的流量确实真实存在的。用流量评价服务的稳定性和效率,可能比评价业务复杂度所体现的技术价值,或许更加真实些。

废话有点多了,上面的问题有个印象。然后遇到问题的时候,大致有个方向再去定位,也许会比较快。下面是如何定位问题的一个思路,流量 -->   服务调用关系链 --> 系统瓶颈 --> 代码 Debug。

处理问题,一定要遵循的第一原则是消除影响,评估印象和追责都是后面的事情。重要的是保证服务恢复。

重要的事情说3遍!恢复稳定,减少影响,不行就先重启一下。

在这里,还是要简单的描述一下海量服务下的柔性可用。理论性的比较多,其实,我遇到了一次流量暴涨的情况告警,CPU 和 内存的 超配比 500% 是一种怎么样的体验,直接爆表了。但是得益于体系的优势,索性平稳过关。海量服务其实并不可怕,做好准备一切都不是问题。

不要有中心节点,这个很重要的!一地部署的结果就是看天! 有一次听微信支付一个大佬分享的时候,他说的一句话我至今还记得:成功是无数个偶然造就的,失败是必然!所以,单机节点服务早晚是会出现问题的,这个只是时间,只是你遇到还是我遇到。去中心化是对用户服务的最基本要求。如果对用户的产品后台的中心节点是一地部署,那业务就危险了。

监控前面已经讲了:APM,让子弹在飞一会。

柔性折损:保证自己的服务活下去是最基本的,所以,谁知伴随的返回码设计——要通用并且有意义。Nodejs 大部分都提供的是 HTTP 的接入层,因此,接口的车功率检测,返回码的上报,服务内部尽量要有打底的数据,防止依赖的服务出了问题,导致无法有效的返回数据。

过载保护:这个和柔性可用差不多,监控资源的瓶颈,到了一个阶段,就要做一些牺牲。该牺牲什么,跟产品讨论一个优先级。不过,大家都是大厂了。我一直相信开源比截流重要,因此,业务如果能多赚钱,机器这点成本,真的不算什么。至于这么小气吗?

防雪崩:返回码约定~不要在来调用了;流控~拒绝服务请求;服务自动扩容~现在那个团队服务不能自动扩容,我觉得开发内心是崩溃的;防雪崩不仅是自己,也要保护好下游服务,不能做不负责任的开发,保护好下游也是你的责任。有问题了,看是不是降低调用频率,使用内存的数据,或者切到其他地区。然后,采用检测返回成功率的形式去尝试,看依赖服务是否好了,在慢慢把服务流量提升,或者切回来。

上面大部分场景,我几乎都经历过,不管是直接的还是间接的。比如:后台被客户端调挂了了,运气比较好,服务不是我写的,如果那个业务是我的,多半也是会挂的。 主要的确没经验。见多了事故和问题的时候,经验就多了,就不担心了。就知道小心了。谁的人生不出几个事故呢?那,真的不是完整的体现。

我觉得遇到一些高级错误,还是值得赞扬的!比如,你使用 mysql 的方法,把它搞 崩溃了,触发了它的系统 BUG。写 java 的时候,触动了一个奇怪的 GC 异常,线程崩了。那还是值得的,这些人都是行业先驱!下面这些,咨询了一些老司机,收集了一批事故清单,然后梳理了一下事故问题的原因,整理了一些解决方案,希望对大家一些帮助。可以在业务上线前尽量的规避风险。

遇到问题,能回滚则回滚。性能瓶颈出现时,缓存就是第一秘诀,内存缓存不行,就换分布式缓存。代码少写同步,尤其 Nodejs 单线程,读写文件的时候,尽量写异步。线上出了问题,有条件的,能扩容,赶快扩容。有时间的时候,就把服务拆分的更合理一点,接口快慢分离,业务读写分离,数据动静分离。

进程重启不要慌,回滚代码是良方!如果业务无法回滚了,那你就只能死扛到底了。如果出现了 CPU 100%,说明测试不够完整;如果是 GC crash ,大概率是内存泄漏了,这个时候,说明你没有压测,灰度时间也不够长;出现进程异常了,那估计代码 P0 用例,应该都没过完整。

接口问题,查看日志和染色,看看前后端是否数据协议没对齐。估计是没给默认数据,或者类型不对。测试场景不够全面,代码质量上尽量用解构赋值。

流量暴涨,估计是有活动发布了,却没有提前通知你,或者业务被刷了,注意服务做好柔性可用,以及自动扩容。还有,压测!压测!作为后台开发,心里没点谱可真的不行。服务 QPS 多一点就挂了的这种,应该拎出去打板子。

写死配置最危险,Code Review 是良药。边界问题最麻烦,完整的测试用例是救星。

硬件设备也会紧张,日志过多、流量过载、IO读写异常,都有可能成为服务的瓶颈。

后记:

欢迎加入 IMWeb 前端团队。

点击阅读原文查看腾讯IMWeb官网。

文章来源于腾讯云开发者社区,点击查看原文