SpringBoot监控基础使用
本文介绍了使用Prometheus和夜莺Nightingale(n9e)来搭建Springboot应用的监控
1. 介绍
1.1 Actuator介绍
Actuator是SpringBoot提供的一个功能组件,可以把服务内部的一些监控指标暴露出来供其他服务使用,包含了常用的监控指标,例如常用的各种健康、指标、配置、日志、线程、内存等信息
| ID | 功能 | 示例 |
|---|---|---|
| health | 服务监控状态 | |
| info | 服务信息 | {} |
| mappings | 请求路径映射 | my.monitor.controller.HelloController#hello() |
| prometheus | Prometheus格式 | # TYPE tomcat_sessions_rejected_sessions_total counter tomcat_sessions_rejected_sessions_total 0.0 # HELP jvm_classes_loaded_classes The number of classes that are currently loaded in the Java virtual machine # TYPE jvm_classes_loaded_classes gauge jvm_classes_loaded_classes 8261.0 # HELP disk_total_bytes Total space for path # TYPE disk_total_bytes gauge |
1.2 Promethues介绍
Prometheus 是开源时序数据库 + 监控采集系统,以时间序列形式存储监控指标,支持灵活的 PromQL 查询、多维度标签筛选、告警规则配置,生态完善,是微服务监控标配。
指标格式:指标名{标签key=标签value} 数值,可按实例、应用、接口路径、请求状态多维度筛选。
jvm_classes_loaded_classes 8261.0
上面的例子就是一个指标,表明JVM加载了8261个类,同时可以给这个指标打一些标签,例如instance表示是哪台机器
2. 使用
2.1 服务改动
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
加入application配置
management.endpoints.web.exposure.include=health,info,prometheus,mappings
management.endpoints.web.exposure.exclude=env,beans,configprops,httpexchanges,threaddump,loggers,auditevents,shutdown
management.endpoint.prometheus.enabled=true
management.metrics.web.server.request.metrics.tag.uri=true
management.metrics.web.server.request.metrics.tag.method=true
management.metrics.web.server.request.metrics.tag.status=true
management:
endpoints:
web:
exposure:
include: health,info,prometheus,mappings
exclude: env,beans,configprops,httpexchanges,threaddump,loggers,auditevents,shutdown
endpoint:
prometheus:
enabled: true
metrics:
web:
server:
request:
metrics:
tag:
uri: true
method: true
status: true
请求
curl http://localhost:8080/actuator/prometheus
可以看到已经有一些指标,/hello这个路径被请求了1次,状态是200
http_server_requests_seconds_count{error="none",exception="none",method="GET",outcome="SUCCESS",status="200",uri="/hello",} 1.0
http_server_requests_seconds_sum{error="none",exception="none",method="GET",outcome="SUCCESS",status="200",uri="/hello",} 0.016061625
http_server_requests_seconds_count{error="none",exception="none",method="GET",outcome="SUCCESS",status="200",uri="/actuator",} 1.0
http_server_requests_seconds_sum{error="none",exception="none",method="GET",outcome="SUCCESS",status="200",uri="/actuator",} 0.033224
http_server_requests_seconds_count{error="none",exception="none",method="GET",outcome="SUCCESS",status="200",uri="/actuator/mappings",} 1.0
http_server_requests_seconds_sum{error="none",exception="none",method="GET",outcome="SUCCESS",status="200",uri="/actuator/mappings",} 0.069719458
2.2 Prometheus和夜莺
2.2.1 Prometheus使用docker本地安装
- 编辑本地配置文档
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
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"]
# The label name is added as a label `label_name=<label_value>` to any timeseries scraped from this config.
labels:
app: "prometheus"
- job_name: 'my-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ["host.docker.internal:8080"]
- 启动
docker run --name prometheus -d -p 9090:9090 -v ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
- 可以在本地查询到对应的指标

2.2.2 夜莺n9e
2.2.2.1 夜莺安装
部署,如果是windows或者mac需要安装文档的描述本地编译一下,需要自行研究一下怎么安装go语言,启动后界面

2.2.2.2新增数据源
也就是我们刚刚启动的prometheus

2.2.2.3 查询指标
和prometheus内置的查询一样,同样使用了PromQL语言

2.2.2.4 配置仪表盘
这边直接导入一个内置仪表盘,可以查看到JVM相关的监控

当然,也可以自定义配置一些仪表盘,需要了解如何查询指标,和PromQL的相关知识
2.2.2.5 配置告警

夜莺告警核心三要素:
- 通知媒介:配置邮件、飞书 / 钉钉企业微信机器人、短信等告警发送渠道
- 通知规则:配置接收人、告警级别、静默时间、发送模板
- 告警规则:基于 PromQL 配置阈值触发条件,如 CPU 过高、接口错误率飙升、服务下线
这块可以参考n9e的相关文档
进阶可搭配 FlashDuty 实现告警收敛、排班、事件认领、降噪等企业级能力。
2.3 常用的PromQL
接口总 QPS
sum(rate(http_server_requests_seconds_count[1m])) by (uri,method,status)
接口 P95 响应耗时
histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[1m])) by (le,uri))
5xx 错误请求数
sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m])) by (uri)
JVM 堆内存使用
jvm_memory_used_bytes{area="heap"}
JVM 已加载类数量
jvm_classes_loaded_classes
3. 其他
3.1 安全防护
在使用SpringBoot的Actuator功能中,我们通过HTTP将指标暴露出来,但是这些指标会有一些敏感信息,不想让用户或者开发人员获取,可以给这个接口增加鉴权,最基础的可以使用一个过滤器来实现
IP 白名单 + 固定 Token 拦截器
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
@Component
public class ActuatorAuthInterceptor implements HandlerInterceptor {
// 允许访问的内网IP
private static final List<String> ALLOW_IP = Arrays.asList("127.0.0.1","192.168.1.100");
// 访问固定token
private static final String AUTH_TOKEN = "monitor@2026";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String requestUri = request.getRequestURI();
// 只拦截 actuator 接口
if (!requestUri.startsWith("/actuator")) {
return true;
}
String clientIp = getIpAddr(request);
String token = request.getHeader("X-Monitor-Token");
// 校验IP + Token
if (ALLOW_IP.contains(clientIp) && AUTH_TOKEN.equals(token)) {
return true;
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
本文来自博客园,作者:songtianer,转载请注明原文链接:https://www.cnblogs.com/songjiyang/p/19986067

浙公网安备 33010602011771号