本文将以Spring Boot/Spring Cloud为例,介绍如果使用Prometheus SDK实现自定义监控指标的定义以及暴露,并且会介绍Prometheus中四种不同指标类型(Counter, Gauge, Histogram, Summary)的实际使用场景;
前言
Prometheus社区提供了大量的官方以及第三方Exporters,可以满足Prometheus的采纳者快速实现对关键业务,以及基础设施的监控需求。
如上所示,一个简单的应用以及环境架构。一般而言,我们通常会从几个层面进行监控指标的采集:
- 入口网关:这里可以是Nginx/HaProxy这一类的负载均衡器,也可以是注入Spring Cloud Zuul这一类框架提供的微服务入口。一般来说我们需要对所有Http Request相关的指标数据进行采集。如请求地址,Http Method,返回状态码,响应时长等。从而可以通过这些指标历史数据去分析业务压力,服务状态等信息。
- 应用服务:对于应用服务而言,基本的如应用本身的资源使用率,比如如果是Java类程序可以直接通过JVM信息来进行统计,如果是部署到容器中,则可以通过Container的资源使用情况来统计。除了资源用量外,某些特殊情况下,我们可能还会对应用中的某些业务指标进行采集。
- 基础设施:虚拟机或者物理机的资源使用情况等。
- 其它:集群环境中所使用到的数据库,缓存,消息队列等中间件状态等。
对于以上的集中场景中,除了直接使用Prometheus社区提供的Exporter外,不同的项目可能还需要实现一些自定义的Exporter用于实现对于特定目的的指标的采集和监控需求。
扩展Spring应用程序,支持Prometheus采集
添加Prometheus Java Client依赖
这里使用0.0.24的版本,在之前的版本中Spring Boot暴露的监控地址,无法正确的处理Prometheus Server的请求,详情:https://github.com/prometheus/client_java/issues/265
1 | # build.gradle |
启用Prometheus Metrics Endpoint
添加注解@EnablePrometheusEndpoint启用Prometheus Endpoint,这里同时使用了simpleclient_hotspot中提供的DefaultExporter该Exporter会在metrics endpoint中放回当前应用JVM的相关信息
1 |
|
默认情况下Prometheus暴露的metrics endpoint为 /prometheus,可以通过endpoint配置进行修改
1 | endpoints: |
启动应用程序访问 http://localhost:8080/metrics 可以看到以下输出:
1 | # HELP jvm_gc_collection_seconds Time spent in a given JVM garbage collector in seconds. |
添加拦截器,为监控埋点做准备
除了获取应用JVM相关的状态以外,我们还可能需要添加一些自定义的监控Metrics实现对系统性能,以及业务状态进行采集,以提供日后优化的相关支撑数据。首先我们使用拦截器处理对应用的所有请求。
继承WebMvcConfigurerAdapter类,复写addInterceptors方法,对所有请求/**添加拦截器
1 |
|
PrometheusMetricsInterceptor集成HandlerInterceptorAdapter,通过复写父方法,实现对请求处理前/处理完成的处理。
1 | public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter { |
自定义Metrics指标
Prometheus提供了4中不同的Metrics类型:Counter,Gauge,Histogram,Summary
Counter:只增不减的计数器
计数器可以用于记录只会增加不会减少的指标类型,比如记录应用请求的总量(http_requests_total),cpu使用时间(process_cpu_seconds_total)等。
对于Counter类型的指标,只包含一个inc()方法,用于计数器+1
一般而言,Counter类型的metrics指标在命名中我们使用_total结束。
1 | public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter { |
使用Counter.build()创建Counter metrics,name()方法,用于指定该指标的名称 labelNames()方法,用于声明该metrics拥有的维度label。在preHandle方法中,我们获取当前请求的,RequesPath,Method以及状态码。并且调用inc()方法,在每次请求发生时计数+1。
Counter.build()…register(),会像Collector中注册该指标,并且当访问/metrics地址时,返回该指标的状态。
通过指标io_namespace_http_requests_total我们可以:
- 查询应用的请求总量
1 |
|
- 查询每秒Http请求量
1 | # PromQL |
- 查询当前应用请求量Top N的URI
1 | # PromQL |
Gauge: 可增可减的仪表盘
对于这类可增可减的指标,可以用于反应应用的当前状态,例如在监控主机时,主机当前空闲的内容大小(node_memory_MemFree),可用内存大小(node_memory_MemAvailable)。或者容器当前的cpu使用率,内存使用率。
对于Gauge指标的对象则包含两个主要的方法inc()以及dec(),用户添加或者减少计数。在这里我们使用Gauge记录当前正在处理的Http请求数量。
1 | public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter { |
通过指标io_namespace_http_inprogress_requests我们可以直接查询应用当前正在处理中的Http请求数量:
1 | # PromQL |
Histogram:自带buckets区间用于统计分布统计图
主要用于在指定分布范围内(Buckets)记录大小(如http request bytes)或者事件发生的次数。
以请求响应时间requests_latency_seconds为例,假如我们需要记录http请求响应时间符合在分布范围{.005, .01, .025, .05, .075, .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10}中的次数时。
1 | public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter { |
使用Histogram构造器可以创建Histogram监控指标。默认的buckets范围为{.005, .01, .025, .05, .075, .1, .25, .5, .75, 1, 2.5, 5, 7.5, 10}。如何需要覆盖默认的buckets,可以使用.buckets(double… buckets)覆盖。
Histogram会自动创建3个指标,分别为:
- 事件发生总次数: basename_count
1 | # 实际含义: 当前一共发生了2次http请求 |
- 所有事件产生值的大小的总和: basename_sum
1 | # 实际含义: 发生的2次http请求总的响应时间为13.107670803000001 秒 |
- 事件产生的值分布在bucket中的次数: basename_bucket{le=”上包含”}
1 | # 在总共2次请求当中。http请求响应时间 <=0.005 秒 的请求次数为0 |
Summary: 客户端定义的数据分布统计图
Summary和Histogram非常类型相似,都可以统计事件发生的次数或者发小,以及其分布情况。
Summary和Histogram都提供了对于事件的计数_count以及值的汇总_sum。 因此使用_count,和_sum时间序列可以计算出相同的内容,例如http每秒的平均响应时间:rate(basename_sum[5m]) / rate(basename_count[5m])。
同时Summary和Histogram都可以计算和统计样本的分布情况,比如中位数,9分位数等等。其中 0.0<= 分位数Quantiles <= 1.0。
不同在于Histogram可以通过histogram_quantile函数在服务器端计算分位数。 而Sumamry的分位数则是直接在客户端进行定义。因此对于分位数的计算。 Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源。相对的对于客户端而言Histogram消耗的资源更少。
1 | public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter { |
使用Summary指标,会自动创建多个时间序列:
- 事件发生总的次数
1 | # 含义:当前http请求发生总次数为12次 |
- 事件产生的值的总和
1 | # 含义:这12次http请求的总响应时间为 51.029495508s |
- 事件产生的值的分布情况
1 | # 含义:这12次http请求响应时间的中位数是3.052404983s |
使用Collector暴露业务指标
除了在拦截器中使用Prometheus提供的Counter,Summary,Gauage等构造监控指标以外,我们还可以通过自定义的Collector实现对相关业务指标的暴露
1 |
|
CustomExporter集成自io.prometheus.client.Collector,在调用Collector的register()方法后,当访问/metrics时,则会自动从Collector的collection()方法中获取采集到的监控指标。
由于这里CustomExporter存在于Spring的IOC容器当中,这里可以直接访问业务代码,返回需要的业务相关的指标。
1 | import io.prometheus.client.Collector; |
当然这里也可以使用CounterMetricFamily,SummaryMetricFamily声明其它的指标类型。
小结
好了。 目前为止,启动应用程序,并且访问 http://localhost:8080/metrics。我们可以看到如下结果。
这部分分别介绍了两种方式,在Spring应用中实现对于自定义Metrics指标的定义:
- 拦截器/过滤器:用于统计所有应用请求的情况
- 自定义Collector: 可以用于统计应用业务能力相关的监控情况
同时介绍了4中Metrics指标类型以及使用场景:
- Counter,只增不减的计数器
- Gauge,可增可减的仪表盘
- Histogram,自带buckets区间用于统计分布统计图
- Summary, 客户端定义的数据分布统计图
恭喜,接下来在后面的文章中我们会尝试将应用程序部署到Kubernetes当中,并且通过Prometheus采集其数据,通过PromQL聚合数据,并且在Grafana中进行监控可视化。