- 是什么:Prometheus / Grafana 都是系统监控工具,Grafana 有更丰富的图表功能
- 做什么:Prometheus 可以采集系统暴露出来的指标数据,Grafana 可以将这些数据转换为丰富的图表,两者都可以做监控,两者结合做监控大盘
效果
监控
告警
搭建项目
- 继续上文的项目,添加数据库连接
非必要步骤,只是这里加上之后,在下文的 Dashboard 中可以看到连接池信息
pom.xml
...
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
...
配置数据库连接
application.yml
...
spring:
datasource:
url: jdbc:mysql://localhost:3306/java-framework-study?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&&useSSL=false&&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
minimum-idle: 5
maximum-pool-size: 15
auto-commit: true
idle-timeout: 60000
pool-name: java-framework-study-hikari-pool
max-lifetime: 1800000
connection-timeout: 3600000
mybatis-plus:
global-config:
banner: off
启动项目可以看到已经创建了数据库连接池
...
2022-11-08 15:51:51.555 INFO 15024 --- [-192.168.42.190] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-11-08 15:51:51.556 INFO 15024 --- [-192.168.42.190] com.zaxxer.hikari.HikariDataSource : java-framework-study-hikari-pool - Starting...
2022-11-08 15:51:51.559 INFO 15024 --- [-192.168.42.190] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
2022-11-08 15:51:51.728 INFO 15024 --- [-192.168.42.190] com.zaxxer.hikari.HikariDataSource : java-framework-study-hikari-pool - Start completed.
- 添加 Prometheus,暴露相关数据
pom.xml
...
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
...
依赖添加之后 Spring Boot 会自动配置一个 PrometheusMeterRegistry 和 CollectorRegistry 来收集和输出格式化的 metrics 数据,使得 Prometheus 服务可以获取
任意访问一个 controller 接口,然后查看 prometheus 暴露出来的信息
因为上文中我们配置了暴露全部 Endpoint,所以添加配置之后不需要更改配置文件,如果你不是这样配置的请在配置文件中暴露 prometheus
[root@39b1ab8e279c /]# curl -s -X GET 'http://172.31.96.34:8080/test?time=1'
SUCCESS
[root@39b1ab8e279c /]# curl -s -X GET 'http://172.31.96.34:8080/actuator/prometheus'
...
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/actuator/prometheus",} 2.0
http_server_requests_seconds_sum{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/actuator/prometheus",} 0.8790499
# HELP http_server_requests_seconds_max
# TYPE http_server_requests_seconds_max gauge
http_server_requests_seconds_max{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/actuator/prometheus",} 0.8033505
...
# HELP hikaricp_connections_timeout_total Connection timeout total count
# TYPE hikaricp_connections_timeout_total counter
hikaricp_connections_timeout_total{pool="java-framework-study-hikari-pool",} 0.0
...
已经可以看到 hikaricp 的相关信息了
配置 Prometheus
上文中我们的 Spring Boot 应用已经暴露了相关的指标信息,接下来使用 Prometheus 采集这些信息
这里使用 Docker 方式部署
docker run -d --name prometheus -p 9090:9090 -v D:\Proj\DOCKER\prometheus\data:/opt/bitnami/prometheus/data -v D:\Proj\DOCKER\prometheus/prometheus.yml:/opt/bitnami/prometheus/conf/prometheus.yml bitnami/prometheus:latest
访问 http://localhost:9090 , 服务已经启动
- 配置 Target(收集目标),编辑我们创建容器时映射的配置文件 prometheus.yml
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: [ 'localhost:9090' ]
- job_name: 'spring-actuator'
# 指定路径
metrics_path: '/actuator/prometheus'
# 收集数据的时间间隔
scrape_interval: 5s
static_configs:
# 需要监控的应用节点
- targets: [ '172.31.96.34:8080' ]
我们配置了两个 job:prometheus/spring-actuator,分别监控 Prometheus 自己暴露的 metric 信息和 Spring Boot 应用暴露的信息,重启容器访问 http://localhost:9090/targets
配置 Grafana
Grafana 将 Prometheus 收集到的信息已丰富多彩的图标样式展示出来
- 官网介绍了不同的安装方式,这里直接下载 Windows 安装包,然后启动
访问 http://localhost:3000, 默认的帐号密码 admin/admin
- 添加数据源,菜单选择 Configuration -> Data Source -> Add Data Source,配置 Prometheus 作为数据源
填入 Prometheus 地址,然后点击保存
- 添加 Dashboard
或者从官网搜索:grafana.com/grafana/das…,然后根据 ID 导入
三种方式,上传 JSON 文件,输入 ID,或者输入 JSON 内容
点击导入,查看 Dashboard
自定义 Dashboard
变量
上文使用模板添加的 Dashboard 中,第一栏是一些变量的筛选
这些变量的筛选规则可以在设置中看到
如果你 Definition 的数据是空的,点击变量进去就能看到定义
我们可以在应用中自定义一些 tag,比如
application.yml
...
spring:
application:
name: spring-boot-actuator
...
management:
metrics:
tags:
application: ${spring.application.name}
重启应用之后收集到的信息中就会多一个 tag
[root@39b1ab8e279c /]# curl -s -X GET 'http://172.31.96.34:8080/actuator/prometheus'
# HELP hikaricp_connections_pending Pending threads
# TYPE hikaricp_connections_pending gauge
hikaricp_connections_pending{application="spring-boot-actuator",pool="java-framework-study-hikari-pool",} 0.0
...
因为模板的变量定义有问题,这里修改 application 的定义
label_values(jvm_classes_loaded_classes{instance="$instance"}, application)
保存之后,application 字段变有值了
自定义指标
有些情况我们还需要自定义一些监控指标,比如最常见的订单成交量
在动手开发之前我们需要先理解几个指标的类型
- Counter:只增不减的计数器,只有在系统重启的时候 Counter 会发生重置
- Gauge:可增可减的仪表盘
根据他们的特性,我们使用 Counter 统计下单量,使用 Gauge 统计库存量,再来是指标名称的定义,官网给出了指标名称的最佳实践,简言之:
- 应该有一个前缀代表这个指标所属应用,如:
- prometheus_notifications_total: (specific to the Prometheus server)
- process_cpu_seconds_total: (exported by many client libraries)
- http_request_duration_seconds: (for all HTTP requests)
- 有一个后缀,代表这个指标的单位,如:
- http_request_duration_seconds
- node_memory_usage_bytes
- http_requests_total (for a unit-less accumulating count)
- 单位参考 Base units
- tag 的值应该是个枚举,而不是类似 userId 这种无穷的数据
因此我们将指标定义为:app_order
/app_goods_stock
- 定义指标
@Component
@Getter
@RequiredArgsConstructor
public class OrderMonitor {
private final MeterRegistry registry;
private Counter orderCounter;
private AtomicInteger appGoodsStock;
@PostConstruct
public void init() {
orderCounter = registry.counter("app_order");
// 自定义 tag
// orderCounter = registry.counter("app_order", Tags.of("tagName", "tagValue"));
appGoodsStock = registry.gauge("app_goods_stock", new AtomicInteger(100));
}
}
- 编写控制器
@RestController("/order")
@RequiredArgsConstructor
public class OrderController {
private final OrderMonitor orderMonitor;
@PostMapping("/buy")
public String buy() {
// 订单量增加
orderMonitor.getOrderCounter().increment();
// 库存减少
orderMonitor.getAppGoodsStock().decrementAndGet();
return "下单成功";
}
@PostMapping("/return")
public String returnI() {
// 库存增加
orderMonitor.getAppGoodsStock().incrementAndGet();
return "退货成功";
}
}
- 应用重启之后访问 http://172.31.96.34:8080/buy 一次,观察
/prometheus
暴露的数据
[root@39b1ab8e279c /]# curl -s -X GET 'http://172.31.96.34:8080/actuator/prometheus'
...
# HELP app_order_total
# TYPE app_order_total counter
app_order_total{application="spring-boot-actuator",} 1.0
...
# HELP app_goods_stock
# TYPE app_goods_stock gauge
app_goods_stock{application="spring-boot-actuator",} 99.0
...
可以看到生成了 app_order_total counter
和 TYPE app_goods_stock gauge
两个指标,分别代表项目启动之后订单成交量,以及商品库存
- 在 Grafana 中制作图表,我们使用仪表盘展示库存,使用折线图展示订单成交变化率
如图配置仪表盘
app_goods_stock{application="$application",}
我们配置了一个阈值,当库存低于 90,仪表盘会变成红色
如图配置订单变化率
irate(app_order_total{instance="$instance", application="$application"}[5m])
接下来我们再请求 10 次下单接口,可以看到库存和订单变化率
- 如果你需要定制自己的图表 PromQL 就会是一门必修课
查询时间序列
# 会返回该 metric 的所有时间序列
http_requests_total
# 等价
http_requests_total{}
# 过滤tag
http_requests_total{instance="localhost:9090"}
# 使用变量过滤
http_requests_total{instance="$instance"}
# 使用label=~regx表示选择那些标签符合正则表达式定义的时间序列;
# 使用label!~regx进行排除
http_requests_total{environment=~"staging|testing|development",method!="GET"}
范围查询
# 选择最近5分钟内的所有样本数据
http_requests_total{}[5m]
- s - 秒
- m - 分钟
- h - 小时
- d - 天
- w - 周
- y - 年
时间位移操作
# 5 分钟前的瞬时样本数据
http_request_total{} offset 5m
# 昨天一天的区间内的样本数据
http_request_total{}[1d] offset 1d
聚合操作
# 查询系统所有http请求的总量
sum(http_request_total)
# 按照mode计算主机CPU的平均使用时间
avg(node_cpu) by (mode)
# 按照主机查询各个主机的CPU使用率
sum(sum(irate(node_cpu{mode!='idle'}[5m])) / sum(irate(node_cpu[5m]))) by (instance)
内置函数
increase 函数获取区间向量中的第一个和最后一个样本并返回其增长量
# Counter类型指标的增长率
increase(node_cpu[2m]) / 120
rate 函数可以直接计算区间向量 v 在时间窗口内平均增长速率
# 和上文 increase 等价
rate(node_cpu[2m])
irate 函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率,可以体现瞬时变化情况
irate(node_cpu[2m])
设置告警
Prometheus 和 Grafana 都可以设置告警,本文使用 Grafana 设置告警
- 添加通知方式
根据需要选择一种通知方式进行添加
- 添加告警规则,回到 Dashboard,编辑订单成交变化率
如图是因为告警规则中不允许包含变量,而我们的表达式中包含了两个变量
添加一个规则专门用于告警,点击小眼睛使得图标不展示这条查询
# 订单量变化率
irate(app_order_total{}[5m])
设置告警规则
- Evaluate every 5s For 10s:每 5 秒计算一次,如果 Condition 成立,告警会进入 Pending 状态,如果持续了 10 秒,则发送告警通知
- WHEN min() OF query(B, 10s, now) IS ABOVE 0:当
query(B, 10s, now)
的所有样本的最小值大于 0 时,Condition 成立 - query(B, 10s, now):从现在开始,10 秒内,满足 query B 的样本数据
这里为了测试时间间隔设置都是秒为单位,具体根据情况设置查询间隔
当订单变化率连续 10 秒钟大于 0 时,钉钉会收到告警,我们 10 秒内断续下单接口,10 秒后,钉钉会收到告警