监控
监控
监控是Atom非常有特色的一个中间件,我们都知道一般的企业系统,都有统一的监控平台。用户监控软件的系统指标和业务指标,监控让我们可以直观看到系统的内部运行状态。 不过监控体系其实是一个比较复杂的基础服务,这是因为监控的存储不是mysql这类关系型数据库,所以一般来说公司都是统一搭建云平台采集监控指标。
所以Atom目前是唯一能看到的,支持标准的监控指标API、且不依赖第三方服务的监控方案。
Atom内置的监控体系,不依赖任何外部服务,他完全是系统内嵌,你不需要做任何部署配置便在内部拥有几乎完整的监控功能。这非常适合个人开发者和独立软件。 这也是Atom作为单体软件的一个特点,即任何中间件都是独立和内置的。
指标采集
Atom指标采集使用的行业标准API:io.micrometer
,在大家的工作经验中,应该经常使用此API。同时作为基础API,我也将它封装到全局, 这样可以在任何地方方便的使用他。这个封装就是:cn.iinti.atom.service.base.metric.monitor.Monitor
更多的指标相关API,大家可以参考标准的资料:Micrometer 快速入门
存储和渲染
和标准的普罗米修斯+grafana
技术组合不同,在指标存储(对标普罗米修斯)和渲染(对标grafana)方面,Atom完全实现了自己的存储和渲染引擎。 当然这只是因为普罗米修斯和grafana都是需要独立部署的服务,而Atom希望自己的服务不依赖三方系统。保持运维简单和降低最终客户使用难度。
实际上由于Atom采用的标准
io.micrometer
执行指标采集,故使用传统的普罗米修斯+grafana
技术路径也是完全没有问题的。Atom默认配置了相关的指标接口, 可以用于对接标准的普罗米修斯指标服务。如果你的系统最终运行在你们公司内部,则依然可以将指标接入到公司监控平台。
在存储方面,Atom将指标存储到mysql中,当然指标模型本身是时序数据,mysql作为关系型数据库无法保持良好性能,也无法支持复杂的指标计算函数。所以我们说 Atom内置的监控体系是几乎支持常用的监控功能,但是对于非常复杂的场景还是存在缺陷。
- 指标精度:Atom不支持长久高精度,比如Atom不支持保持一年前的分钟指标,Atom将会定期将指标进行模糊和融合,如此降低mysql存储压力。故对于长期数据,你只能看到一个相对模糊的数据
- 指标类型:目前Atom只支持Counter、Guage、Timer三种指标,其他标准的指标类型将会在存储的适合被过滤
- 函数:在指标渲染过程,一般会通过书写ql语句,调用运算函数,实现多种指标的叠加计算,在最终监控面板渲染复合的图标。然而Atom在函数方面只能支持少量的函数(不过几乎够用)
- 零值处理:Atom不支持区分零值和缺值,即当一个指标没有打印的时间点,指标加工引擎将会按零值处理。这导致在报警的时候,无法区分好正确的零值和缺值
对于渲染方面,Atom的指标在前端页面执行渲染,我们封装好了前端的组件,在前端直接调用组件即可构建对应的监控图表。如下:
const SystemMetrics = () => {
return (<MetricPage configs={[{
title: "负载(Load)",
mql: `
服务器分钟负载 = metric(system.load.average.1m);
服务器分钟负载 = aggregate(服务器分钟负载,'serverId')
show(服务器分钟负载);
系统CPU使用率 = metric(system.cpu.usage);
系统CPU使用率 = aggregate(系统CPU使用率,'serverId')
进程CPU使用率 = metric(process.cpu.usage);
进程CPU使用率 = aggregate(进程CPU使用率,'serverId')
show(系统CPU使用率,进程CPU使用率);
`
}, {
title: "jvm内存",
bottomLegend: true,
mql: `
jvm内存 = metric(jvm.memory.used);
jvm内存 = aggregate(jvm内存,'area','serverId');
show(jvm内存);
`
} ]}/>);
}
MQL
Atom使用的监控体系是独立设计的,其中MQL是Atom独立开发的、用于操作和加工监控指标的一门微型语言(metric query language) 一个类似的demo如下:
# 负载
服务器分钟负载 = metric(system.load.average.1m);
show(服务器分钟负载);
系统CPU使用率 = metric(system.cpu.usage);
进程CPU使用率 = metric(process.cpu.usage);
show(系统CPU使用率,进程CPU使用率);
语法介绍
- 注释: "#"开头的行为注释行,只支持单行注释
- 语句:MQL由多个语句组成,语句顺序执行,不支持分支和循环。语句通过换行(\n)或者分号(;)分割
- 变量:用于承载指标,以及被加工后的指标,MQL变量只有一种类型(即一个指标,或者被加工产生的新指标)。不支持其他类型变量,如数字、字符串等
- 函数:MQL支持调用函数,但是不支持自定义函数。MQL的函数都是系统内置的。MQL的参数类型均是字符串,并且由逗号(,)分割多个参数
- 四则运算:MQL支持指标的四则运算(加减乘除)
- 运算分量数据类型
- 数字:均以double解析
- 函数调用结果:四则运算表达式可以嵌套函数调用
- 变量:即在前面的语句中被定制过的指标变量
- 除零处理:在发生四则运算除零时,其计算结果是零
- 括号和优先级:和普通语言一样,复合四则运算表达式满足优先级:
乘除 > 加减
,当需要手动调整优先级是,使用括号()
控制 - 过滤器:对指标进行降维过滤,如只取某个服务器节点的指标数据:
a = a[serverId='xxxx.xxx.xxx'] + 1;
- 运算分量数据类型
请注意,过滤器其实是一个函数调用的包装语法糖,上述案例可以表达为:
a = filter(a,serverId,'xxxx.xxx.xxx') +1;
多维和tag
所有指标在采集的时候都可以指定多个维度的分量(即tag
)。tag表示指标在多个业务分量上面的情况,如http请求,区分请求的api前缀、http的状态码、多台服务器节点条件下对应的服务器ID, 此时这个指标具备三个维度,记录这三个维度的指标代码如下:
Monitor.counter("http.someApi.access","api","/user/profile","httpStatus","200","serverNode","192.168.23.34").increment();
此时本指标表达了发生在API、状态码、服务器三个维度的事件。
多维聚合和过滤
多维指标在运算的时候可以存在聚合和过滤两种运算,他们是指标加工的基础操作。
- 聚合:当我们不关心维度详情的时候,可以执行聚合操作。如上述指标,我们可能不关心
serverNode
的情况,则可以在服务器分量上面执行聚合操作。此时聚合流程会在这个维度上面进行叠加计算,计算结果将会删除这个tag,即三维降低到二维。此时指标含义为http接口在API前缀、状态码两个维度上的情况。而不关心数据来自那台服务器。 - 过滤:当我们只关心某个维度特定值的详情的时候,可以执行过滤操作。如上述指标,我们可能只关心特定服务器的情况(
serverNode
),则可以在服务器分量上面执行过滤操作。a = a[serverId='xxxx.xxx.xxx']
,过滤结果的含义为,仅在这一台服务器上,http接口的调用情况,并且保留他们在API前缀、状态码的分布情况。过滤操作也是一个降维动作,因为过滤之后本维度即消失(请注意,当前MQL不支持过滤条件in操作)
timer多维和转换
timer在底层存储将会自动添加一个维度:timer_type,因为timer本来就是一个复合指标。
- count: 记录指标事件发生次数
- time:所有事件执行事件总和
- max:执行事件最长的事件
当对timer进行过滤的时候,其指标类型将会由timer转换为counter或者gauge,这是因为分维过滤之后,其语义已经不是timer了
函数列表
- aggregate:指标聚合,多维聚合降维操作
- dropLeft:裁剪左侧数据,当一个业务刚开始启动,其指标量不稳定,无法表达正常的业务水位,此时删除一段时间的指标数据
- filter:过滤,多维过滤降维操作,用户观察某个业务分量的指标情况
- getVar:获取一个变量的值,即根据一个字符串变量得到对应的MQLVar
- metric:从mysql数据库中获取一个指标,本函数支持谓语过滤,即在获取指标的时候,从数据库完成filter
- shift:指标左移,即把历史某个时刻的指标值作为当前时间的值。本操作多用于计算环比和同步数据,因为本函数的计算结果可以用于当前值和历史值的复合运算
- show:渲染指令,告诉绘图引擎绘制本条指标在界面上。
时间精度切换
受限于mysql的结构化存储能力(非时序数据库),我们的监控系统不具备非常复杂的时间穿梭能力。也即我们无法存储海量的指标,以及提供海量指标的快速查询和计算
当一个指标样本发生了很久的时间,考虑存储成本,我会将长久以前的数据进行模糊化处理(即聚合处理)具体规则如下:
- 系统采样按照分钟采样,每分钟收集所有指标数据
- 对于发生在两天前的分钟采样数据,将它聚合在小时窗口。即某个小时内的60个时间点的所有事件数据,聚合模糊到单个数据分量,如此其指标精度只能查看这个小时的整体概况,而再也无法观察这个小时内某分钟的详细情况
- 对于发生在两月前的小时采样数据,将它聚合在天窗口。即某个天内的24个时间点的所有事件数据,聚合模糊到单个数据分量,如此其指标精度只能查看这一天的整体概况,而再也无法观察这一天内某小时钟的详细情况