SpringBoot+Prometheus:微服务开发中自定义业务监控指标的几点经验

作者:橙色马路

马楠的上一篇文章中,我们已经了解到Prometheus的一大优势,是可以在应用内定义自己的指标做监控。我们在 SpringBoot 做微服务的生产环境中,使用自定义指标监控诸多物联网传感器,时序数据结构简单清晰,监控与统计反应迅捷,效果良好。

SpringBoot 2的actuator中默认使用Micrometer作为指标支持库。本身已经内置了许多开箱即用的指标。自定义的指标注册以后,也会被融合在相同的uri(/actuator/prometheus)中统一输出,非常方便。

1. 注册表(Registry)

Prometheus是用定时Pull方式去服务器拉取指标数据,以拉取的时间打点做时间轴,形成时间序列。应用服务器只需暴露当下时刻的指标值即可,所以数据模型本质是一个个KV键值对,存放在全局的容器,Prometheus来获取的时候,将容器内的值格式化输出。此处的容器即为计量注册表(MeterRegistry) 。

几种内置的Registry如下

SimpleMeterRegistry:极简实现,数据存取在内存中,每项数据只保留最新一次的数值。默认SpringBoot会帮你autowire一个。

CompositeMeterRegistry:用组合模式帮你将多个注册表串联成一个对外接口。全局的Metrics.globalRegistry就是这么个组合模式接口

2. 两种常用指标类型(Metric Type)

gauge: 可增可减计数器,反应某值当前一刻状态。比如称重传感器的当前重量,温度传感器的当前温度。

方式一:

Gauge.builder("gn.temperature.gauge", new AtomicInteger(37), AtomicInteger::get)

方式二:

registry.gauge("gn.temperature.gauge", Tags.of("site", "SiteA", "cab", "cab01"), new AtomicInteger(37));

两者等价,会输出成如下指标:

# HELP gn_temperature_gauge_value for cab temperature
# TYPE gn_temperature_gauge_value
gn_temperature_gauge_value{application="xxx",cab="cab01",site="SiteA",} 37.0

注意定义的名称转换:其中"."被换成了"_",gauge类型的指标最后加上了"_value"做结尾。指标命名只能为ASCII字母、数字、下划线和冒号,且必须配正则表达式[a-zA-Z_:][a-zA-Z0-9_:]*

counter:只增不减计数器,是Gauge的一个特例。适用于只有服务器重启时候才会重置的计数场景。比如"用户访问次数",某接口失败次数"等等。API 使用方式类似。

Counter counter = Counter.builder("gn.beat.counter")
  .tags("site", "SiteA", "function", "foo")
  .description("for request errors")
  .register(registry);

counter.increment();

会输出成如下指标:

# HELP gn_beat_counter_total for request errors
# TYPE gn_beat_counter_total counter
gn_beat_counter_total{application="xxx",function="foo",site="SiteA",} 1.0

3. 融入到系统的方式

方式一,业务系统埋点:

@Component
public class SampleBean {
    private final Counter counter;

    public SampleBean(MeterRegistry registry) {
        this.counter = registry.counter("gn.beat.counter");
    }

    public void handleMessage(String message) {
        this.counter.increment();
        // handle message implementation
    }
}

方式二:MeterBinder

SpringBoot中提供了MeterBinder接口用于申明与注册meterRegistry。自定义Metrics只需要实现MeterBinder接口,Spring会自动发现并完成后续的杂活。

@Bean
public class MyMetrics implements MeterBinder {
   @Override
   public void bindTo(MeterRegistry meterRegistry) {
    //此处添加相关指标
    meterRegistry.gauge("gn.temperature.gauge", Tags.of("site", "SiteA", "cab", "cab01"), new AtomicInteger(37));
   }
}

Spring优秀的解耦架构还可以方便地搭配自定义配置项方式使用。

4. 垃圾回收与NaN

指标值注册到registry中默认为弱引用,若函数调用调用周期结束,则该值会被 Java 给标记并 GC 掉。对应的指标输出的值则会很快会变成了NaN。

此种短状态适用于心跳类型的指标,在预警系统中可以及时发现没有按时上报的点。

但对于相对长时间想保持住特定指标值,需要显式给到对应变量强引用。比如使用一个实例化的HashMap来 cache 相关的值。

如上段代码中,方式一为强引用,方式二则弱引用。

5. 指标设计与选型

名称 + {一组tag} + 值 为一指标形式。

如某库位温度指标形式如下:

gn_temperature_gauge_value{application="xxx",cab="cab01",site="SiteA"} 37.0

tag中值为 String 型,尽量选取『可识别』,『有限集合』的值作为 tag,比如主机,柜号,库位等等。每一个tag值都会产生一个维度,不同的 tag值+名称会被记录成不同的时间序列。

某些很有潜力膨胀到"无限量"的值如各种ID、邮件地址、时间戳等,就不适宜选做 tag的值

后期若使用 grafana 绘制特定指标变化图,不同时间序列也会对应到不同的多条曲线。

指标值为 double 型: 对于自定义的数值型,如温度,访问次数等的指标,原样输出即可。

若输入某些状态类的值,可定义成数值型。比如 Prometheus 中的存活状态指标 UP = 1。类似如某后台 Job 状态(pending, running, stopped),可依样定义为10,20,30。

6. 集群部署下多微服务指标汇聚

对于Prometheus这类基于Pull模式的监控平台而言,往往由中心server来决定采集的目标有多少有哪些,所以在微服务在集群模式下,多端Metrics的聚合可通过Prometheus的Pull模式天然汇聚。

通过服务发现形式,获取到当前集群中所有节点的信息,更新配置文件并且重建抓取的列表,将分散的Metrics聚合到中心服务器。

Prometheus本身支持 DNS, Consul, Kubernetes, OpenStack, EC2 等发现机制。在Kubernetes下,可通过与Kubernetes API集成目前主要支持多服务发现模式,如Node、Service、Pod、Endpoints、Ingress等。

部署架构的示意图如下:

这部分由部署层实现,对应用层透明。

7. 关于作者

橙色马路:创业者,技术管理与架构,ex-Oracler。

相关参考:

https://micrometer.io/docs/concepts

https://spring.io/blog/2018/03/16/micrometer-spring-boot-2-s-new-application-metrics-collector

https://winderresearch.com/introduction-to-monitoring-microservices-with-prometheus/

本文分享自微信公众号 - 云服务与SRE架构师社区(ai-cloud-ops)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券