一、client_java
client_java是Prometheus针对JVM类开发语言的client library库
,我们可以直接基于client_java用户可以快速实现独立运行的Exporter程序
,也可以在我们的项目源码中集成client_java以支持Prometheus。注意:Prometheus 提供的client_java 项目可以很方便的将 JVM 和自定义的指标暴露出来,具体可以监控的指标参考
二、配置文件
- pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.code</groupId>
<artifactId>prometheus_test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>prometheus_test</name>
<description>prometheus_test</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--这个依赖用于健康检查,审计,指标收集-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--这个依赖用于把数据转换为prometheus格式使用 r-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- yaml文件
spring:
application:
name: springboot-prometheus
management:
metrics:
#允许对应的数据指标被导出
export:
prometheus:
enabled: true
#激活 prometheus 并转换为对应的 prometheus 数据
endpoint:
prometheus:
enabled: true
health:
show-details: always
metrics:
enabled: true
#对外暴露哪些指标
endpoints:
web:
exposure:
include: "*"
# 自定义监控地址的路径来避免被别人轻易的就访问到,更安全的做法是将其端口同服务的端口区分开,并禁止外网访问 原始基路径/actuator
base-path: /customize-actuator
# actuator暴露接口使用的端口,为了和api接口使用的端口进行分离,用于禁止外网访问
server:
port: 8085
三、自定义Collector
在client_java的simpleclient模块中提供了自定义监控指标的核心接口。simpleclient模块由micrometer-registry-prometheus依赖导入
。当无法直接修改监控目标时,可以通过自定义Collector的方式,实现对监控样本收集,该收集器需要实现collect()方法并返回一组监控样本
,如下所示:
public class CustomExporter extends Collector {
@Override
public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<>();
//创建metrics指标
GaugeMetricFamily labeledGauge =
new GaugeMetricFamily("io_namespace_custom_metrics", "custom metrics", Collections.singletonList("labelname"));
//设置指标的label以及value
labeledGauge.addMetric(Collections.singletonList("labelvalue"), 1);
mfs.add(labeledGauge);
return mfs;
}
}
这里定义了一个名为my_guage的监控指标,该监控指标的所有样本数据均转换为一个MetricFamilySamples.Sample实例
,该实例中包含了该样本的指标名称、标签名数组、标签值数组以及样本数据的值。监控指标my_guage的所有样本值,需要持久化到一个MetricFamilySamples实例中
,MetricFamilySamples指定了当前监控指标的名称、类型、注释信息等。需要注意的是MetricFamilySamples中所有样本的名称必须保持一致,否则生成的数据将无法符合Prometheus的规范。
@Component
public class PromeConfig {
@Resource
private CollectorRegistry collectorRegistry;
@Bean
@Primary
public CustomCollector customCollector() {
return new CustomCollector().register(collectorRegistry);
}
}
访问:http://localhost:8085/customize-actuator/prometheus
可以看到自定义的collector被Prometheus所统计监控
四、使用内置的Collector
通过client_java中定义的标准接口,用户可以快速实现自己的监控数据收集器,并通过HTTPServer将样本数据输出给Prometheus。除了提供接口规范以外,client_java还提供了多个内置的Collector模块,以simpleclient_hotspot为例
,该模块中内置了对JVM虚拟机运行状态(GC,内存池,JMX,类加载,线程池等)数据的Collector实现。
<!-- https://mvnrepository.com/artifact/io.prometheus/simpleclient_hotspot -->
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>0.16.0</version>
</dependency>
通过调用io.prometheus.client.hotspot.DefaultExport的initialize方法注册该模块中所有的Collector实例:
DefaultExports.initialize();
重新运行CustomExporter,并获取样本数据
$ curl http://127.0.0.1:1234/metrics
# HELP jvm_buffer_pool_used_bytes Used bytes of a given JVM buffer pool.
# TYPE jvm_buffer_pool_used_bytes gauge
jvm_buffer_pool_used_bytes{pool="direct",} 8192.0
jvm_buffer_pool_used_bytes{pool="mapped",} 0.0
除了之前自定义的监控指标以外,在响应内容中还会得到当前JVM的运行状态数据。在client_java项目中除了使用内置了对JVM监控的Collector以外,还实现了对Hibernate,Guava Cache,Jetty,Log4j、Logback等监控数据收集的支持
。用户只需要添加相应的依赖,就可以直接进行使用。
五、在业务代码中进行监控埋点
- Prometheus中4种监控类型
- Counter,只增不减的计数器
- Gauge,可增可减的仪表盘
- Histogram,自带buckets区间用于统计分布统计图
- Summary, 客户端定义的数据分布统计图
基于这些实现,开发人员可以非常方便的在应用程序的业务流程中进行监控埋点
。
- Counter:计数器是表示单个单调递增计数器的累积量,
其值只能增加或在重启时重置为零。
- 服务的请求数量
- 已完成的任务数
- 出现的错误总数
一般而言,Counter类型的metrics指标在命名中我们使用_total
结束。
public class PrometheusMetricsInterceptor implements HandlerInterceptor {
@Autowired
private MeterRegistry meterRegistry;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 请求计数+1,标签分别为 请求路径,请求方法,response http code
meterRegistry.counter("http_requests_total", Tags.of("url", request.getRequestURI(), "method", request.getMethod(),"status",String.valueOf(response.getStatus()))).increment();
}
不要使用计数器来监控可能减少的值。 例如,不要使用计数器来处理当前正在运行的进程数,而应该用Gauge。
- Gauge:Gauge可以用来
存放一个可以任意变大变小的数值,通常用于测量值Gauge可以任意加减
- 温度
- 内存
- cpu
- 线程数
public class PrometheusMetricsInterceptor implements HandlerInterceptor {
@Autowired
private MeterRegistry meterRegistry;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 处理中计数 +1
meterRegistry.gauge("http_process_req", Tags.of("url", request.getRequestURI(), "method", request.getMethod()), 1);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 请求完毕,正在请求计数器-1
meterRegistry.gauge("http_process_req", Tags.of("url", request.getRequestURI(), "method", request.getMethod()), -1);
}
}
- Histogram:直方图,内置分析样本的分布情况
- 请求持续时间
- 响应大小
- 区间数据分组统计
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}中的次数时。
public class PrometheusMetricsInterceptor implements HandlerInterceptor {
@Autowired
private MeterRegistry meterRegistry;
private ThreadLocal<Timer.Sample> threadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Timer.Sample sample = Timer.start();
threadLocal.set(sample);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Timer histogramTimer = Timer.builder("http_req_histogram")
//publishPercentiles-用于发布你的应用中计算出的百分比值。这些值在跨维度上都是不可聚合
.publishPercentiles(0.5,0.95)
//publishPercentileHistogram-用于发布直方图,可用于计算跨纬度的百分比近似值聚
.publishPercentileHistogram()
//控制publishPercentileHistogram桶的数量,同时控制基础HdrHistogram结构的准确性和内存占用量。
.minimumExpectedValue(Duration.ofMillis(1))
.maximumExpectedValue(Duration.ofMinutes(3))
.sla(Duration.ofMillis(10), Duration.ofMillis(50), Duration.ofMillis(100), Duration.ofMillis(300), Duration.ofMillis(1000))
.tags(Tags.of("url", request.getRequestURI(), "method", request.getMethod(), "code", String.valueOf(response.getStatus())))
.register(meterRegistry);
//Timer的使用可以基于它的内部类Timer.Sample,通过start和stop两个方法记录两者之间的逻辑的执行耗时
threadLocal.get().stop(histogramTimer);
threadLocal.remove();
}
}
Histogram会自动创建3个指标,分别为:
- 事件发生总次数:
basename_count
# 实际含义: 当前一共发生了23次http请求
http_req_histogram_seconds_count{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",} 23.0
- 所有事件产生值的大小的总和:
basename_sum
# 实际含义: 发生的23次http请求总的响应时间为 0.0174146秒
http_req_histogram_seconds_sum{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",} 0.0174146
- 事件产生的值分布在bucket中的次数:
basename_bucket{le=”上包含”}
# 在总共23次请求当中。http请求响应时间 <=0.01 秒 的请求次数为23
http_req_histogram_seconds_bucket{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",le="0.01",} 23.0
http_req_histogram_seconds_bucket{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",le="0.05",} 23.0
http_req_histogram_seconds_bucket{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",le="0.1",} 23.0
http_req_histogram_seconds_bucket{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",le="0.3",} 23.0
http_req_histogram_seconds_bucket{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",le="1.0",} 23.0
http_req_histogram_seconds_bucket{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",le="+Inf",} 23.0
- Summary:摘要 客户端定义的数据分布统计图
- 观察总和
- 观察计数
- 排名估计
Summary和Histogram非常类型相似,都可以统计事件发生的次数或者发小,以及其分布情况。Summary和Histogram都提供了对于事件的计数_count以及值的汇总_sum。 因此使用_count,和_sum时间序列可以计算出相同的内容
,例如http每秒的平均响应时间。同时Summary和Histogram都可以计算和统计样本的分布情况,比如中位数,9分位数等等。其中 0.0<= 分位数Quantiles <= 1.0。不同在于Histogram可以通过histogram_quantile函数在服务器端计算分位数。 而Sumamry的分位数则是直接在客户端进行定义。因此对于分位数的计算。 Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源
。相对的对于客户端而言Histogram消耗的资源更少
public class PrometheusMetricsInterceptor implements HandlerInterceptor {
@Autowired
private MeterRegistry meterRegistry;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
DistributionSummary summary = DistributionSummary
.builder("response_size")
.description("response_size") // 可选
.baseUnit("bytes") // 可选 添加基本单元,可以最大程度的获取可移植性—对于某些监控系统而言,基本单元是命名规则的一部分。如果不设置,也不会有什么不利影响。
.tags(Tags.of("url", request.getRequestURI(), "method", request.getMethod(), "code", String.valueOf(response.getStatus()))) // 可选
.scale(100) // 可选 设置比例因子,这样在记录样本的时候都会乘以这个比例
.register(meterRegistry);
summary.record(response.getBufferSize());
}
}
# 含义:当前http请求发生总次数为7次
response_size_bytes_count{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",} 7.0
# 含义:这7次http请求的总响应体buffer.size为5734400.0
response_size_bytes_sum{application="springboot-prometheus",code="200",method="GET",url="/prometheus/test2",} 5734400.0
参考文章
参考文章
参考文章
参考文章
参考文章