本文转载自微信公众号《代码秘密花园》(code_mmhy)。作为前端工程师,你每天还要维护一些Node.js服务。对于一个服务,我们首先要注意的是它的稳定性。可能大部分同学对服务器端的很多概念都没有很深入的了解,所以在稳定性方面不知道要注意什么。在本文中,我将首先介绍一些我会关注的关于服务器稳定性的指标。整体分为两大方面:资源稳定性:即当前服务所在的运行环境的一些指标。一般情况下,如果资源稳定性指标不是问题,那么服务可能出现了较大的问题,甚至处于不可用状态。服务运行稳定性:服务运行过程中产生的异常、日志、延迟等。一、资源稳定性1、CPU(1)CPULoadCPULoad是CPU的负载,表示一段时间内CPU正在处理和等待CPU处理的进程数总和的统计信息.当CPU完全空闲时,CPULoad为0,CPU工作越饱和,CPULoad越大。如果CPU每分钟最多处理100个进程,则系统负载为0.2,也就是说CPU在这1分钟内只处理了20个进程。借用阮一峰的例子:我们把CPU想象成一座桥,桥上只有一条车道,所有车辆都必须通过这条车道。系统负载为0意味着桥上没有车辆。0.5的系统负载意味着一半的桥梁被车辆占用。1.0的系统负载意味着桥的所有段都充满了流量,这意味着桥是“满的”。然而,必须指出的是,在此之前,这座桥仍然可以通车。系统负载为1.7,表示车辆过多,桥已满(100%),等候上桥的车辆占桥上车辆的70%。如果容器有2个CPU,则意味着系统负载可以达到2.0,每个CPU达到100%的工作负载。一般来说,对于有n个CPU的计算机来说,最大可接受的系统负载是n。多核CPU的效果和多CPU类似,所以在考虑系统负载的时候,一定要考虑这台电脑有多少个CPU,每个CPU有多少核。(2)CPUUsageCPUUsage表示程序占用CPU时间片的情况,也就是我们常说的CPU利用率。它可以反映一定采样时间内的CPU使用率,入住率百分比从两个角度。一般情况下,CPUUsage高,CPULoad也会比较高。CPUUsage越低,CPULoad就越低。也有例外:CPULoadlow,CPUUsagehigh:如果CPU执行的任务数量少,CPULoad就会低,但那些任务是CPU密集型的,那么利用率就会高。HighCPULoadandlowCPUUsage:如果CPU执行大量任务,CPULoad会很高,但CPU在任务执行过程中经常处于空闲状态(比如等待IO),利用率会很低。2.Memory(1)MemoryRSSRSS:ResidentSetSize用来表示系统分配给当前进程的内存大小,可以包括所有的栈和堆内存,是OOM的主要指标。(1)MemoryV8Heap表示JavaScript代码执行占用的内存。一般我们可以看到V8Heap区分了Used和Total。这主要是因为V8的内存归还机制。进程中的一些内存是可回收的,不会立即回收。Total-Used其实是指当前可回收的内存。但是没有回收内存。(3)内存max-old-space-sizeV8允许的最大老年代内存大小,可以简单的认为是一个Node.js进程可以长期维持的最大内存大小。当进程的HeapTotal接近这个值时,进程很可能因为V8abort而退出。(4)ExternalNode.js中的MemoryBuffer是基于V8Uint8Array封装的,所以在Node.js中使用Buffer时,其内存使用量会记录在External中。另外,在Node.js中很少使用externalstring,所以我们可以认为对于一个普通的Node.jsweb应用,process.memoryUsage()中的External主要是指Buffer占用的内存大小。Buffer常用于Node.js中IO相关的API,如:文件操作、网络通信等。3.LibuvLibuv是一个跨平台的库,封装了操作系统的IO操作。Node.js使用Libuv作为自己的事件循环,uv负责IO操作。net、dgram、fs、tty等模块,Timer等类都可以认为是基于uv的封装。因此,uv相关的数据指标一定程度上可以反映Node.js应用的稳定性。(1)LibuvHandleslibuvhandles表示Node.js进程中各种IO对象(tcp、udp、fs、timer等)的个数。对于常见的web应用,libuvhandles高通常意味着当前请求量大或者有tcp连接没有正确释放。之前在线上业务中,经常会发现句柄没有关闭,比如:tcp和udpsocket不断创建,没有关闭,导致操作系统端口被耗尽的问题。(2)LibuvLatencylibuv延迟不是可以从libuv或Node.jsAPI直接获取的数据。目前主流的libuvlatency计算方法是通过setTimeout()设置定时器,将调用回调函数时消耗的时间与估计消耗时间的差值记录为latency,如:constkInterval=1000;conststart=getCurrentTs();setTimeout(()=>{constdelay=Math.max(getCurrentTs()-start-kInterval,0);},kInterval);较高的延迟值通常意味着当前应用程序的事件循环太忙。结果,无法按时完成简单的操作。对于Node.js进程来说,这种情况很可能是由于调用了一个长时间的同步函数或者阻塞IO操作导致的。当出现这种问题时,相应的线程将无法进行正常的服务。比如对于http服务器,这段时间的请求是不会被响应的。因此,我们需要保证主线程的libuv延迟尽可能小。二、服务运行的稳定性1、状态码这个不用多说了。服务产生的所有5xx状态码都属于服务器试图处理请求时的内部错误。这些错误可能是服务器本身的错误,而不是请求错误。,所有这些都需要我们注意:500(InternalServerError)服务器遇到错误,无法完成请求。501(Notyetimplemented)服务器无法完成请求。例如,当服务器无法识别请求方法时,可能会返回此代码。502(错误网关)作为网关或代理的服务器从上游服务器收到无效响应。503(ServiceUnavailable)服务器当前不可用(由于超载或停机维护)。通常,这只是一个临时状态。504(GatewayTimeout)服务器正在充当网关或代理,但没有及时收到上游服务器的请求。505(HTTPVersionUnsupported)服务器不支持请求中使用的HTTP协议版本。506由《透明内容协商协议》(RFC2295)扩展,表示服务器存在内部配置错误:请求的协商变量资源被配置为在透明内容协商中使用自身,因此不是协商过程中的适当焦点。507服务器无法存储完成请求所必需的内容。这种情况被认为是暂时的。509服务器达到带宽限制。这不是官方状态代码,但仍被广泛使用。510不满足获取资源所需的策略。错误日志服务运行过程中产生的错误日志数量也是衡量一个服务是否稳定的重要指标。对于错误日志上报,不同公司的业务实现可能不同,但应该大同小异。一般日志分为INFO、WARN、ERROR几个级别,我们需要注意ERROR及以上级别的日志。通常,在我们的业务逻辑中,我们需要捕获并报告服务运行过程中产生的异常,但我们不可能捕获到程序运行的所有节点上的异常。另外,trycatch也不是万能的,它不能捕获异步异常,所以我们一般在使用的Node.js框架中将日志上报与关键节点集成在一起。以KOA为例,我们需要监听app的error事件:this.on('error',(error,ctx)=>{if(error.status===404){return;}constmessage=error.stack||error.message;log(message);});另外,我们还需要在uncaughtException和unhandledRejection报告异常:process.on('unhandledRejection',(error)=>{if(error){log({level:'error',location:'[gulu-core]::UnhandledRejection',message:error.stack||error.message,});}});process.on('uncaughtException',(error)=>{log({level:'error',location:'[gulu-core]::UncaughtException',message:error.stack||error.message,});process.exit(1);});执行完这样的操作之后,你的业务逻辑中产生的所有异常都会被捕获并上报,所以对于你想知道的异常你不应该手动去尝试捕获,而是丢给框架去捕获上报。2.pm2log对于我们在程序中打印出来的一些控制台,一般生产环境默认是不会记录的。比如我们可能会通过trycatch自己捕获一些程序异常,然后在控制台输出ERRORINFO。此类异常不会被捕获为错误日志。一般在线运行的Node服务都是使用PM2启动的。pm2是一个节点进程管理工具,可以用来简化节点应用管理的很多繁琐工作,比如性能监控、自动重启、负载均衡等,我们可以使用pm2log命令查看实时日志当前运行的程序。请注意,此日志包括开发人员自己键入的一些控制台。另外,pm2还支持查看所有历史日志。我们可以通过Error等关键字来检索错误日志。3、时延时延也是衡量一个服务稳定性的重要指标。一些非常慢的界面除了影响用户体验外,还可能影响数据库的稳定性。一般我们比较接口的延迟和数据库的延迟。两个方面重点关注服务延迟,这个比较容易理解,这里就不多说了。4、QPSQPS:全称QueriesPerSecond,意思是“每秒的查询率”。它是服务器每秒可以响应的查询数。它是特定查询服务器在指定的每秒查询数内处理的流量。指标。简单的说,QPS=req/sec=请求数/秒。它代表服务器机器的性能最大吞吐量。一般来说,服务的QPS会随着时间的推移有规律的上升或下降,但是如果QPS在一定时间内上升了一个数量级,则有可能是你的服务受到了DDoS攻击,或者被非法调用。
