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本地安装

  1. 编辑本地配置文档
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"]
  1. 启动
docker run --name prometheus -d -p 9090:9090 -v ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
  1. 可以在本地查询到对应的指标

image-20260506205954691

2.2.2 夜莺n9e

2.2.2.1 夜莺安装

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

image-20260507103840534

2.2.2.2新增数据源

也就是我们刚刚启动的prometheus

image-20260507104031918

2.2.2.3 查询指标

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

image-20260507104137583

2.2.2.4 配置仪表盘

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

image-20260507104318779

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

2.2.2.5 配置告警

01

夜莺告警核心三要素:

  1. 通知媒介:配置邮件、飞书 / 钉钉企业微信机器人、短信等告警发送渠道
  2. 通知规则:配置接收人、告警级别、静默时间、发送模板
  3. 告警规则:基于 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;
    }
}
posted @ 2026-05-07 12:15  songtianer  阅读(37)  评论(0)    收藏  举报