关于Prometheus
Prometheus是一个根据应用的metrics来进行监控的开源工具。相信很多工程都在使用它来进行监控,有关详细介绍可以查看官网:https://prometheus.io/docs/introduction/overview/。
SpringBoot使用Prometheus
Maven依赖
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_spring_boot</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_logback</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.1.3</version>
</dependency>
配置
application.yml配置
配置文件中加入配置,management.metrics.tags.application
management:
endpoints:
web:
exposure:
include: '*'
metrics:
tags:
application: ${spring.application.name}
prometheus.yml配置
用于在prometheus中增加要监控的服务。Prometheus服务发现的配置分为静态配置和动态发现, 常用的为以下几类,static_configs
: 静态服务发现;file_sd_configs
: 文件服务发现;dns_sd_configs
: DNS服务发现;kubernetes_sd_configs
: Kubernetes服务发现;consul_sd_configs
: Consul服务发现;本文演示为static_configs
;
- job_name: 'moodfly-order'
scrape_interval: 5s
metrics_path: '/Order/actuator/prometheus'
static_configs:
- targets: ['127.0.0.1:18084']
job_name:任务名称 metrics_path: 指标路径 targets:实例地址,如果有多台服务器可配置多个。
配置完毕。分别启动要监控的的服务和prometheus;然后访问prometheus看板和要监控服务的/Order/actuator/prometheus
接口,检验配置是否正确;
open :http://192.168.0.109:18084/Order/actuator/prometheus
出现以下页面显示 证明集成prometheus已经OK。
# HELP tomcat_sessions_rejected_sessions_total
# TYPE tomcat_sessions_rejected_sessions_total counter
tomcat_sessions_rejected_sessions_total{application="moodfly-mall-order",} 0.0
# HELP jvm_gc_max_data_size_bytes Max size of old generation memory pool
# TYPE jvm_gc_max_data_size_bytes gauge
jvm_gc_max_data_size_bytes{application="moodfly-mall-order",} 2.853699584E9
# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use
# TYPE jvm_memory_committed_bytes gauge
jvm_memory_committed_bytes{application="moodfly-mall-order",area="heap",id="PS Survivor Space",} 1.835008E7
Space",} 7.3662464E8
jvm_memory_committed_bytes{application="moodfly-mall-order",area="nonheap",id="Compressed Class Space",} 1.0223616E7
# HELP jvm_threads_peak_threads The peak live thread count since the Java virtual machine started or peak was reset
# TYPE jvm_threads_peak_threads gauge
...
open :http://localhost:9090/
集成完毕
自定义指标监控
SpringBoot集成Prometheus默认使用了actuator的所有指标,但是我们在业务开发中,还是需要去做一些自定义的指标信息的监控,自定义的指标发送到Prometheus根据指标的时序状态,增加系统状态感知、系统健康度监控,闭环异常报警机制及报警治理体系。
Prometheus内部提供的指标类型
-
Counter
类型代表一种样本数据单调递增的指标,只增不减 -
Guage
类型代表一种样本数据可以任意变化的指标,可增可减 -
Histogram
在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图
3.1 样本的值分布在bucket
中的数量,命名为<basename>_bucket{le="<上边界>"}
。解释的更通俗易懂一点,这个值表示指标值小于等于上边界的所有样本数量
3.2 样所有样本值的大小总和,命名为<basename>_sum
3.3 样本总数,命名为<basename>_count
-
Summary
与Histogram
类型类似,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间来计算。
4.1 样本值的分位数分布情况,命名为<basename>{quantile="<φ>"}
4.2 样所有样本值的大小总和,命名为<basename>_sum
4.2 样本总数,命名为<basename>_count
启用@PrometheusTimeMethod
注解 来追踪方法执行用时。被该注解标注的方法,会被收集到Summary指标中;
Enable Spring-AOP-based automated method timing for the annotated method.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface PrometheusTimeMethod {
/**
* The metric name to use for recording latencies
* @return A metric name specific to your use case.
*/
String name();
/**
* The help message to show in prometheus metrics
* @return A help string
*/
String help();
}
EG : 统计下单量 ,在线人数,可以使用 Guage
,Summary
@Autowired
MeterRegistry registry;
private Counter counter_index;
private AtomicInteger app_online_count;
private DistributionSummary summary;
@PostConstruct
private void init(){
app_online_count = registry.gauge("app_online_count", new AtomicInteger(0));
summary = registry.summary("Order.create");
}
public Object onlineCount(Double orderCount){
int people = 0;
try {
people = new Random().nextInt(2000);
app_online_count.set(people);
summary.record(orderCount);
} catch (Exception e){
return e;
}
return "current online people: " + people;
}
PS :上面就是个演示。实操参考思路:可以配合AOP +自定义注解 来实现一个指标收集层,针对方法级别增加标注,在初始化之后,注册多维指标到MeterRegistry
。
PromQL查询
基础查询和常用内置函数
- 范围查询
eg: 查询最近5分钟内的所有样本数据: http_request_total{}[5m] 可以通过向花括号({})里附加一组标签来进一步过滤时间序列。
可选单位:s - 秒
,m - 分钟
,h - 小时
,d - 天
,w - 周
,y - 年
- 时间位移操作(offset)
- eg: 查询5分钟前的样本数据: http_request_total{} offset 5m
- eg: 查询昨天1天内的样本数据: http_request_total{}[1d] offset 1d
- 聚合操作
- sum (求和)
- min (最小值)
- max (最大值)
- avg (平均值)
- stddev (标准差)
- stdvar (标准差异)
- count (计数)
- count_values (对value进行计数)
- bottomk (后n条时序)
- topk (前n条时序)
- quantile (分布统计)
其中只有 count_values
, quantile
, topk
, bottomk
支持参数(parameter)
。
without
用于从计算结果中移除列举的标签,而保留其它标签。by 则正好相反,结果向量中只保留列出的标签,其余标签则移除。通过 without
和 by
可以按照样本的问题对数据进行聚合。
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
- 内置函数
- increase(v range-vector):获取区间向量中的第一个和最后一个样本并返回其增长量 用于统计增长数。
- rate(v range-vector):计算区间向量v在时间窗口内平均增长速率 最常用
- irate(v range-vector):计算区间向量v在时间窗口内平均增长速率(瞬时增长率,基于最后两个数据)
- predict_linear(v range-vector, t scalar):预测时间序列v在t秒后的值
- abs(v instant-vector): 返回输入向量的绝对值
- sort(v instant-vector): 按升序排列
- sort_desc(v instant-vector): 按降序排列
- delta(v range-vector) 的参数是一个区间向量,返回一个瞬时向量。它计算一个区间向量 v 的第一个元素和最后一个元素之间的差值。
- histogram_quantile(φ float, b instant-vector) 从 bucket 类型的向量 b 中计算 φ (0 ≤ φ ≤ 1) 分位数(百分位数的一般形式)的样本的最大值,一般用来统计分位图。
histogram_quantile
这个函数是根据假定每个区间内的样本分布是线性分布来计算结果值的(也就是说它的结果未必准确),最高的 bucket 必须是 le="+Inf" (否则就返回 NaN)。
如果分位数位于最高的 bucket(+Inf) 中,则返回第二个最高的 bucket 的上边界。如果该 bucket 的上边界大于 0,则假设最低的 bucket 的的下边界为 0,这种情况下在该 bucket 内使用常规的线性插值。
如果分位数位于最低的 bucket 中,则返回最低 bucket 的上边界。
匹配模式
一对一匹配
一对一匹配模式会从操作符两边表达式获取的瞬时向量依次比较并找到唯一匹配(标签完全一致)的样本值。默认情况下,使用表达式:
vector1 <operator> vector2
在操作符两边表达式标签不一致的情况下,可以使用 on(label list) 或者 ignoring(label list)来修改便签的匹配行为。使用 ignoreing 可以在匹配时忽略某些便签。而 on 则用于将匹配行为限定在某些便签之内。
<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr>
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在所有请求中的比例。如果没有使用 ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。
多对一和一对多
多对一和一对多两种匹配模式指的是“一”侧的每一个向量元素可以与"多"侧的多个元素匹配的情况。在这种情况下,必须使用 group 修饰符:group_left 或者 group_right 来确定哪一个向量具有更高的基数(充当“多”的角色)。
<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr>
多对一和一对多两种模式一定是出现在操作符两侧表达式返回的向量标签不一致的情况。因此需要使用 ignoring 和 on 修饰符来排除或者限定匹配的标签列表。
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
该表达式中,左向量 method_code:http_errors:rate5m
包含两个标签 method 和 code。而右向量 method:http_requests:rate5m
中只包含一个标签 method,因此匹配时需要使用 ignoring 限定匹配的标签为 code。 在限定匹配标签后,右向量中的元素可能匹配到多个左向量中的元素 因此该表达式的匹配模式为多对一,需要使用 group 修饰符 group_left 指定左向量具有更好的基数。
提醒:group 修饰符只能在比较和数学运算符中使用。在逻辑运算 and,unless 和 or 操作中默认与右向量中的所有元素进行匹配。
高级统计查询样例
- JVM 宕机
up == 0
- JVM 堆内存占用
jvm_memory_bytes_used{job="java", area="heap"} / jvm_memory_bytes_max * 100
- 过去5分钟JVM GC回收用时
increase(jvm_gc_collection_seconds_sum{gc="PS MarkSweep"}[5m])
- 过去5分钟JVM GC停顿时间增长率
rate(jvm_gc_pause_seconds_count{gc="PS MarkSweep"}[5m])
- 获取某个应用的活跃线程树数目
jvm_threads_live_threads{namespace="order"}
- 一周前的每5分钟之内的 HTTP 请求量的平均增长率:
rate(http_server_requests_seconds_count[5m] offset 1m))
- 返回区间向量中每个时间序列过去 5 分钟内 HTTP 请求数的增长数:
increase(http_server_requests_seconds_count {}[5m])
- 返回区间向量中过去1小时内每5分钟内 HTTP 请求数的增长率大于0.2的:
rate(http_server_requests_seconds_count[5m] offset 1h) > 0.2
- 最近 10 分钟之内 90% 的
handler="/"
的请求量的最大值histogram_quantile(0.9, rate(prometheus_http_request_duration_seconds_bucket{handler="/"}[10m]))
- 高阶聚合:根据job标签来对90分位
histogram_quantile(0.9,sum(rate(prometheus_http_request_duration_seconds_bucket[10m])) by (job ))
- 最近 1 分钟之内 应用app日志级别是ERROR的含有关键字(***)的总数
sum by(custom_app_name)(increase(metric——name{custom_app_name="app", level="ERROR", keyword="***"}[1m]))
- 最近 5 分钟之内应用app的所属容器的总使用率
sum(app:cpu:total:usage{label_app=\"$app\",container_name=\"\"}[5m]) by (pod)
- 域名可用性
domain:availability:rate2m{domain="www.moodfly.top"}
- 最近3分钟之内JVM的内存增长率
(delta(jvm_memory_bytes_used[3m]) / (jvm_memory_bytes_used offset 3m) * 100)`
- 90分位的某指标的方法执行的时间
avg by (app,class,method) (histogram_quantile(0.9,metric_bucket{method!="methodName"}))*1000
- 基于 2 小时的样本数据,来预测主机可用磁盘空间在 4 个小时之后的剩余情况
predict_linear(node_filesystem_free{job="job"}[2h], 4 * 3600)