为什么要用APM
在讲所有的内容之前。 举个例子:线上App或者是小程序,点击某个按钮没有响应,做为前端 or 运维人员,需要怎么快速排查 + 定位问题。
常规的排查思路:点击按钮,是否触发js错误->检查接口是否正确返回数据/接口是否报错了。
这种情况下:
有些生产环境上的问题不好排查,比如: (1)运营商问题,用户的运营商劫持了js,导致前端程序报错,或者是CDN某个节点的缓存有问题,导致另外一个老版本的js下线了。
(2)用了第三方的服务,比如支付服务,支付服务超时或者额度达到上限欠费了。
(3)机房故障,导致服务挂掉了,不可用。
APM监控服务的示意图: 实际上就是一个应用监控的东西,可以获取到应用的请求路径等。 会在用户出现异常,或者用户主动上报时,会做收集。
APM的形态
(1)对服务里依赖的硬件性能,比如CPU/内存/硬盘容量进行监控。
(2)性能指标的实时值,出现紧急问题时发生告警。
(3)监测服务的情况,如请求数,响应数,成功率。
(4)错误/ 异常监控,需要agent上报。(这个成本比较高,一般需要应用主动上报)
(5)日志收集,对服务的所有情况进行记录,日志是必不可少的,不管是业务本身,还是访问记录,都尽可能的需要日志,日志会占用较多的硬盘空间,所以需要定期清除日志。清除掉以后的问题就比较难以排查了。
(6)中间件相关的依赖监控,对服务的依赖进行监控,如数据库,缓存,外部服务,几乎所有的响应缓慢问题,都是依赖导致的。
(7)真实场景下,分布式事务追踪,监控发起到完成的情况,通过完整的请求路径,用一个唯一的traceId去标识。
分布式事务的追踪实现的原理:
Trace:一次完整请求的链路。
Span:一次调用过程。
SpanContext:Trace的全局上下文信息,如TraceId。

Profiling - 程序级别的监控分析
一般利于自动化的代码插桩,技术,获取nodejs进程内的方法调用链路。 可以查看调用栈上的总执行时间和每个方法占的百分比。 可以结合V8本身的Profiling,查看可能出现的内存泄漏情况。
NodeJs应用 - 0x火焰图的使用-CPU问题排查
火焰图里,越底层的部分,就是C的实现了,上面的都是js的表现。 对于nodejs来说,上层是js代码,下层是C的代码。 这里,楼主定义了一个a方法和b方法,里面实现了两段垃圾代码,下面上代码。
js
const restify = require('restify')
const server = restify.createServer();
function a(){
for(let i = 0;i<1e8 ;i++){}
}
function b(){
for(let i =0;i<2e8;i++ ){}
}
server.get('/',(req,res,next)=>{
a();
b();
res.send({});
next();
})
server.listen(3000,()=>{
console.log('3000 port runing')
})
process.on('SIGINT',()=>{
server.close();
})
a方法执行了1e8次,b方法执行了2e8次,针对这种情况,我在引入了0x(火焰图)工具,做了一些Profiling的手段。
js
"scripts": {
"test":"NODE_ENV=production 0x -P 'autocannon -c 20 http://localhost:3000' ./cpu-frame-graph/app.js"
},
这里我直接yarn test。
他会以20个并发连接访问10s,得到火焰表现图,生产成一个路径存放刚才执行行为的性能分析报告。
上面我们可以清楚的看出来,a方法占用了百分25,b方法占用了百分50,和我们程序的行为一致。
实战中,如果是Nodejs侧的问题,比较容易找的出来,如果是底层涉及到IO事件循环的那块,就比较黑盒了,不太容易能够分析的出来问题。


NodeJs应用 - 0x火焰图的使用-内存的USE指标
首先需要认识一下,NodeJs的USE指标:
Process.memoryUsage可以查看到Nodejs当前进程下占用的内存。
其中:
Rss:操作系统分配给进程总的大小
heapTotal:分配给V8堆的内存大小
heapUse:V8使用的内存大小
external:C系统级库,使用的内存大小 什么情况下会导致OOM? 尤其是在webpack打包的时候,东西比较多,依赖加的比较多的情况下,会导致内存打满的情况。导致CI/CD发布的时候编不过。
nodejs底层V8针对内存使用时有上限的。
64位的操作系统:1.4GB
32位的操作系统:0.7GB
一旦内存的操作使用超过了这个数,就会报错out of memory~
Core和Coredump的问题 内存的结构一圈圈的,被称之为Core。
Coredump:在程序崩溃或者终止的时候,操作系统会把这个行为记录下来打到文件里,这种情况叫做coreDump(内存快照)
排查内存泄漏问题除了查看日志,只能查看Core Dump日志。
NodeJs应用 - 内存监控-内存打满案例
这里推荐全局安装两个工具:
js
npm i autocannon -g
npm i clinic -g
autocannon:压测工具
clinic:监控工具
假设我这里有一段程序。
js
function compute() {
return Buffer.alloc(1e3);
}
这段程序一直在执行,创建Buffer不做回收。
实现了一个run.sh,脚本内容是:
js
set -e
while getopts m:d:f: flag
do
case "${flag}" in
m) method=${OPTARG};;
f) file=${OPTARG};;
d) duration=${OPTARG};;
esac
done
clinic doctor --on-port 'autocannon -w 300 -c 100 -d '$duration' localhost:3000/'$method'' -- node $file
这个时候,我使用
./run.sh -f index.js -m cache -d 20
会自动跳出来仪表盘,这个时候可以看到这个应用的监控。

如果Memory那一栏,呈现了一直在增长的情况,说明存在内存泄漏问题。
长期放任不管,达到阈值,会导致应用崩溃!
NodeJs应用 - QPS/TPS/RT-吞吐量指标
QPS:每一秒能执行多少Query,QPS越高,Server可以处理的请求越多。
RT: 响应时间,执行一个请求开始到结束花费了多少时间。
QPS也可以这么计算
QPS = 并发数/平均响应时间,也可以衡量QPS值。
Graphite(石墨)/Grafana - 应用监控,APM采集。
这里推荐两个开源的APM的日志采集工具。
Graphite是处理可视化和指标的开源工具,提供了很多API和插件,目前开源里比较简单易用的工具。强烈推荐!
Graphite架构说明:my.oschina.net/emacs_89248...
会通过采集器->监听器->存储数据库(whisper),用于存储按照时间序列的数据,通常监控类的数据,数据量是非常庞大的。(时序数据库),但是这个UI比较丑。
Grafana是一款用于Go语言开发的数据可视化工具,可以用做数据监控和统计,自带告警能力,界面比上面的好看,定制化能力也很强,笔主公司目前也是用这个。
Grafana + Graphite搭建业务监控平台教程
实际上部署起来比较方便,没有那么复杂。挺傻瓜化的,一些插件自己按需集成下就行了。
这里的操作,我都是用docker操作的。
docker-compose.yml:
js
version: "3"
services:
grafana:
image: grafana/grafana
ports:
- 3000:3000
graphite-statsd:
image: graphiteapp/graphite-statsd
ports:
- 2003-2004:2003-2004
- 2023-2024:2023-2024
- 8125:8125/udp
- 8126:8126
- 8080:80
graphite-statsd:用于采集。
grafana:用于做看板大盘分析。
运行直接按照下面这个就行了。
js
docker-compose up -d
具体docker以及docker-compose的安装配置过程本文不再过多赘述。
主要是启这两个东西

安装,并且启动起来以后。
直接访问localhost:3000就可以了
grafana默认的账号密码是admin/admin

点击添加,然后把graphite给他关联进去。

配置好之后点Save/Test

程序示例(如何把请求打点):
**express-statsd****是一个用于Connect**和Express**框架的StatsD**路由监控中间件。它能够自动捕获HTTP请求的状态码和响应时间,并将这些数据发送到StatsD服务,以便于监控和性能分析
js
const express = require("express");
const app = express();
const expressStatsd = require("express-statsd");
app.use(expressStatsd());
function statsd(path) {
return function (req, res, next) {
var method = req.method || "unknown_method";
req.statsdKey = ["http", method.toLowerCase(), path].join(".");
next();
};
}
app.get("/", statsd("root"), (req, res) => {
if (between(0, 10) > 5) {
res.status(500).send();
}
res.send("Response from a simple GET API");
});
选择一下规则,筛选出我刚才发送的那些请求。


如何把监控上云-alinode
关于Alinode的介绍:www.aliyun.com/product/nod...
里面自带了监控,告警,内存快照,cpu分析,还有大模型给开发者提供一些优化建议,可以说是非常强大!
本身也是开源框架实现的,开发者可以不用过度关心安全隐私问题等。
可以直接把数据上报到阿里云的平台上

使用的时候直接把runtime部署到我们的nodejs应用里,同样官方也支持eggjs等。
部署文档:
help.aliyun.com/zh/nodejs/u...
实际上,上述说的Grafana已经可以满足绝大部分场景了。
后续笔主有机会会继续分享这一块....