[date: 2018-08-07 18:46] [visits: 39]

Node.js + InfluxDB实现APM

应用性能监控,简称APM(Application Performance Management),它可以让我们随时快速的了解应用所处的状态,以及提供预警等功能。之前只知道有这么个东西但没有接触过,经验基本为零,最近由于业务发展良好并且自己也有兴趣,就赶鸭子上架去试着给系统加一个监控,帮助自己随时了解应用状况。

Metrics

做调研的前期接触到metrics,翻译过来是指标的意思,即数值的呈现,其主要有以下5种类型:

瞬时值,指的是系统中某一个瞬间状态的值,比如系统某一刻的CPU使用率、内存情况等

计数器,如系统的PV、UV等

事件发生速率,如系统的QPS、TPS等

直方图,主要用来统计数据分布情况,比如最小值、最大值、中位数、99百分位等

计时器,网上说是Meters + Historgrams,也就是Meters的分布情况

之所以简单介绍一下Metrics,是因为从零做APM的时候对这里产生了误解,认为应该直接从应用中采集这5种类型的数据上报给APM系统,最终由APM系统通过图表呈现,实际上恰恰相反,Metrics是指APM呈现数值的方式,而不是采集数据的方式,采集只是“反馈时间线的点”。

InfluxDB

因为做APM,第一次接触了时序数据库,经过简单了解后选择了流行的InfluxDB,由于概念与MySQL相似,所以理解起来并不费劲,可以很容易上手使用,这里简述一下它对于监控系统非常有帮助的几个特性。

group by time

时序数据库中每一条数据都有一个时间戳,而group by time可以根据不同的时间跨度对数据进行聚合,例如原始数据分散在任意时刻而我想知道每小时的数量,则可以使用select count(*) from xxx group by time(1h)

保留策略(Retention Policies)

保留策略简称RP,可以理解为表中数据的有效期,类似于Redis中key的有效期。因为监控产生的数据量非常大,不及时删除的话会导致数据量一直增加,硬盘吃不消。

借助这个特性,我们可以对数据进行采样存储到不同的RP中,比如分钟数据放在一周有效期的RP中,小时数据放在一年有效期的RP中,而原始数据可以使用一个24小时的RP。

连续查询(Continuous Queries)

连续查询简称CQ,可理解为Timer + Query,每间隔一定的时长运行指定的Query,使用CQ+RP以及一些influx内部函数,可以非常完美的完成数据整理、采样等操作。

如果应用上报了请求数到request表(influx中称为measurement),我们在数据库中使用select count("value") from "request" group by time(1s) ifnull(0)可以快速查询出每秒的QPS,而结合CQ+RP则可以将原始数据以不同的时间跨度生成不同的Metrics存放到不同的RP中,APM只需要查询采样好的数据并呈现即可。

RP、CQ与group by time是我在使用InfluxDB过程中觉得MySQL不太容易满足的特性,一些网上资料所讲的其他差异,自己没有深入了解。

实现

为了达到应用监控的目的,核心内容包括数据采集、上报、呈现,采集与上报主要发生在应用内部,而接收上报数据则发生在APM系统中。

结合自己的应用架构,这次主要添加两个模块:metrics与apm,其中metrics随业务系统部署完成数据采集,apm则单独部署。

apm接收上报数据后存放到InfluxDB并通过RP+CQ对数据进行采样存储,同时提供HTTP接口支持前端数据可视化。

采集

由于是第一版,所以挑重点关注内容进行采集,采集指标主要包括:

采集request数量用于计算QPS,这个采集可以参考记访问日志功能,因为每一条访问日志即一个请求,所以写一个中间件,每一个请求进行一次采集

采集响应时长用于计算平均值与各种百分值(90、95、99),实现方式同样参考访问日志功能,采集每一个请求的响应时长即可

使用定时器与Node.js内部os模块的loadavg方法实现,由于loadavg最精确也是取1min的值,所以该方式采集的数据并非实时准确

使用定时器与process.memoryUsage方法实现

为了做到与业务解耦,这四个指标的采集使用一个模块与中间件完成,其实业界还有另外一种更为复杂的采集方式:探针,这种方式主要是专门做APM软件的服务商所使用,因为他们要做到更为纯粹的解耦。

调研了一下听云Node.js探针,它内部的实现原理主要是hack Node.js Module上的load方法,然后有针对性wrap一些常用模块的部分方法,如http、express、koa、mysql等。看了大约一个小时的听云探针源码,最终觉得实现成本有点高所以放弃了探针这种方式。

上报

由于高峰期QPS可能达到几百,对于上报的性能要求不容小看,这里参考statsd选择使用UDP,由于UDP是不可靠传输,没有连接的概念,性能相比TCP有一定优势而且出现网络异常概率也会下降,哪怕APM挂了对应用也没啥影响。这里能够选择UDP的核心是因为我觉得应用监控数据有丢失是可接受的,其次才是它相比TCP的一些其他优势。

在选择了UDP之后,还是不满意每一个request都会引起数据上报,因此在这里设计了一个的buffer,用于聚合短时间内的数值,比如每100毫秒上报一次数据,针对这100毫米内的数值来源则分为三种类型:

累加值,比如100毫秒内触发30次request为1的上报,最终只会在100毫秒后以30的数值向APM上报一次

平均值,比如100毫秒内触发30次response-time为5的上报,最终只会在100毫秒后以30 * 5 / 30 = 5的数值向APM上报一次

原始值,不作处理直接上报至APM

我不确定这个buffer有没有必要,但我觉得它对数据处理所带来的影响极低所以就加上了,同时在APM接收端为了批量insert同样设计了一个buffer。

呈现

数据上报到APM存入InfluxDB经过CQ采样后,简单写一两个HTTP API查询InfluxDB就可以满足前端可视化的需求了。

这里我主要将原始数据采样成1s、1m、5m、15m、1h等时段进行存储,然后写了三个API支持不同step与type的查询,接着使用React + ECharts在前端画出四个指标的折线图,APM系统的第一版本顺利搞定。

接下来可能继续添加一两个关心的应用指标,以及考虑使用WebSocket在接收端做一个此刻数据呈现的API,类似linux top的概念。

总结

系统合计做了两三天,主要时间花在调研和思考上面,所有代码加起来估计三五百行,系统上线后稳定运行,数据呈现简直不要太美,哈哈哈。

越来越对偏基础的造轮子工作感兴趣了,接下来还有日志汇总、应用安全Reload、降低sharp rss的问题等着我,加油。