Yujun's Blog

Prometheus + Grafana 实时监控大盘

June 14, 2025 (1mo ago)DevOps

Prometheus + Grafana 实时监控大盘

想让应用性能不再是个玄学问题,就要亲手给它打造一个仪表盘,绝对不能仅靠日志和感觉来判断线上应用的健康状况。这个仪表盘能把所有关键指标——比如实时QPS、CPU占用、内存曲线——都转化为直观的图表。这样,我们就能用数据说话,精确地找到瓶颈、评估容量,而不是靠猜测。

本篇文章主要用于初识 Prometheus + Grafana + JMeter ,并利用它们打造技术报告中一些炫酷、信息量爆炸的监控大盘。 具体步骤如下:

  • 创建一个模拟的 Spring Boot 接口。
  • 使用 Docker 快速部署 Prometheus 和 Grafana。
  • 将 Spring Boot 应用接入监控,并在 Grafana 上创建出专业级的监控图表。

创建被监控的测试应用 (Spring Boot)

创建SpringBoot应用,引入必要的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
	<!--监控:actuator-上报,prometheus-采集,grafana-展示-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

模拟一个接口(或者已有一个真实的接口):

@RestController
public class RaffleController {

    private final Random random = new Random();

    @PostMapping("/api/raffle/draw")
    public String drawRaffle() {
        try {
            // 模拟业务处理耗时,50-150ms
            int processingTime = 50 + random.nextInt(100);
            Thread.sleep(processingTime);

            // 模拟抽奖成功或失败
            if (random.nextInt(10) < 8) { // 80% 概率成功
                return "{\"code\": \"200\", \"message\": \"恭喜你,抽奖成功!\"}";
            } else {
                return "{\"code\": \"500\", \"message\": \"哎呀,库存不足,抽奖失败!\"}";
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "{\"code\": \"503\", \"message\": \"服务器繁忙,请稍后再试!\"}";
        }
    }
}

搭建监控系统 (Prometheus + Grafana)

我们将使用 Docker Compose 来一键启动和管理我们的监控服务,这是目前最流行、最便捷的方式。

如果环境还没有安装的话,在你的docker-compose文件中,添加下面内容:

# 数据采集
  prometheus:
    image: bitnami/prometheus:2.47.2
    container_name: prometheus
    restart: always
    ports:
      - 9099:9090
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - my-network

  # 监控界面
  grafana:
    image: grafana/grafana:10.2.0
    container_name: grafana
    restart: always
    ports:
      - 4000:4000
    depends_on:
      - prometheus
    volumes:
      - ./grafana:/etc/grafana
    networks:
      - my-network

networks:
  my-network:
    driver: bridge

注意:在启动 Prometheus 容器时,prometheus.yml 核心配置文件是至关重要的。 创建 Prometheus 配置文件: 在你的devops目录下,创建一个名为 prometheus 的文件夹,并在其中新建一个 prometheus.yml 文件。这个文件告诉 Prometheus 去哪里抓取监控数据。

# prometheus/prometheus.yml

global:
  scrape_interval: 15s # 每 15 秒抓取一次

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090'] # 抓取 Prometheus 自己的数据

  - job_name: 'spring-boot-app'
    metrics_path: '/actuator/prometheus' # Spring Boot 应用暴露指标的路径
    static_configs:
      # 这里是关键!host.docker.internal 是一个特殊的 DNS 名称,
      # 在 Docker 容器内部,它会解析为你宿主机的 IP 地址。
      - targets: ['172.17.0.1:8080']

它告诉 Prometheus 要去哪里、以什么频率、抓取哪些监控数据。

或者,如果我们有更复杂的场景,比如一个典型的、用于监控一个分布式应用(多实例部署)的 Prometheus 配置:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'big-market-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets:
          - 'big-market-app-01:8091'
          - 'big-market-app-02:8092'
        labels:
          app: 'big-market-app'

这段配置的核心思想是:告诉Prometheus,我这里有一个叫 big-market-app 的监控任务。请你每隔 15 秒,就去访问 big-market-app-01:8091 和 big-market-app-02:8092 这两个地址,记住要去它们的 /actuator/prometheus 路径下拿数据。哦对了,所有从这两个地方拿回来的数据,都请帮我贴上一个 app="big-market-app" 的标签,方便我以后整理和查询。

如果没有自定义的配置文件的话,也就是启动 Prometheus 容器时,没有挂载自己的 prometheus.yml,容器并不会报错。因为它会使用镜像中内置的一个默认的、最小化的 prometheus.yml。这个默认配置文件的内容大致是:

global:
  scrape_interval: 15s
scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

这个配置的唯一作用就是让 Prometheus 每隔 15 秒抓取它自己。 所以,结果就是会得到一个成功运行、但毫无用处的 Prometheus 容器。它能监控自己的健康状况,但对于你的任何其他应用(比如 Spring Boot、MySQL 等)都一无所知。

Grafana可视化

我们要做的就只有在 Grafana 中配置数据源和仪表盘。

登录 Grafana:访问 http://localhost:3000。默认用户名和密码都是 admin。

接着添加数据源:

  • 点击左侧菜单的齿轮图标 (Configuration) -> "Data Sources"。
  • 点击 "Add data source",选择 "Prometheus"。
  • 在 "Prometheus server URL" 中填入 http://prometheus:9090。(注意:因为 Grafana 和 Prometheus 在同一个 Docker 网络中,可以直接使用服务名 prometheus 作为主机名)。
  • 点击 "Save & test",如果显示绿色成功提示,说明连接成功。

最后一步,导入现成的仪表盘。社区里有大量现成的优秀 Spring Boot 监控模板,我们可以直接导入使用:

  • 点击左侧菜单的 “+” (Create) -> "Import"。
  • 在 "Import via grafana.com" 输入框中填入 ID 12856,然后点击 "Load"。这是一个非常受欢迎的 Spring Boot 2 APM 仪表盘。
  • 在下一个页面,选择你刚刚配置的 Prometheus 数据源,点击 "Import"。

现在仪表盘还是空的,因为没有流量。之后制造压力,观察图表。

使用 JMeter 专业压测

现在,我们将使用 Apache JMeter 这个行业标准的压测工具,来模拟大量用户同时抽奖。

前提:请先从 JMeter 官网下载并解压,确保可以运行 bin目录下的jmeter.bat(Windows) 或jmeter.sh(Mac/Linux) 来启动 JMeter 的图形化界面。

步骤 1: 创建基础测试计划

  • 打开 JMeter。
  • 右键点击左侧的“测试计划” -> “添加” -> “线程(用户)” -> “线程组”。

步骤 2: 配置线程组 - 定义虚拟用户规模

线程组用来定义我们将模拟多少用户、以多快的速度、进行多少次测试。

  • ​线程数 (Number of Threads): 填入 50。这代表我们将模拟 50 个并发用户。​
  • ​Ramp-up 时间 (秒): 填入 10。这代表 JMeter 会在 10 秒内,将这 50 个用户全部启动起来(平均每秒启动 5 个),这样可以给服务器一个缓冲,而不是瞬间冲击。​
  • ​循环次数 (Loop Count): 勾选 永远 (Forever)。我们将让这 50 个用户不停地循环抽奖,直到我们手动停止,这样才能在 Grafana 上看到持续的负载。

步骤 3: 添加 HTTP 请求 - 定义用户的任务

现在需要告诉这 50 个用户,他们具体要做什么。

  • 右键点击你创建的“线程组” -> “添加” -> “取样器” -> “HTTP 请求”。

  • ​配置 HTTP 请求:

    • 名称: 可以起个有意义的名字,比如 POST 抽奖接口。
    • ​协议: http
    • 服务器名称或 IP: localhost
    • 端口号: 8080
    • 方法: 非常重要!我们的接口是 POST,所以这里必须选择POST
    • 路径: /api/raffle/draw

步骤 4: 添加监听器 - 在 JMeter 中观察结果

为了确保我们的压测脚本配置正确,并能实时看到结果,我们需要添加两个核心的监听器。

  • ​右键点击线程组 -> 添加 -> 监听器 -> 查看结果树。 ​* ​作用:它会显示出每一次请求的详细信息。压测开始后,我们可以用它来检查最初的几个请求是否成功(显示为绿色),如果失败了,可以方便地查看原因。​

  • ​右键点击线程组 -> 添加 -> 监听器 -> 聚合报告。 ​* ​​​作用:它以表格形式实时统计压测的核心指标,如:样本数、平均响应时间、错误率、以及最重要的 吞吐量 (Throughput),这个值约等于我们常说的 TPS (每秒事务数)

    ​​​

步骤 5: 启动压测。

  • ​保存测试计划:在启动前,JMeter 会提示你保存测试计划,给它起个名字(如 raffle-test.jmx)并保存。​
  • ​启动:点击工具栏上那个绿色的“启动”按钮。

​初步观察:

  • ​切换到查看结果树,应该能看到滚动的绿色请求记录。
  • 切换到聚合报告,会看到吞吐量一列的数字在不断攀升并稳定在一个值附近。
  • 切换到 Grafana :打开 Grafana 仪表盘(ID: 12856 或你自己创建的)。将会看到与 完全不同的景象:
    • ​QPS/RPS 图表:不再是小打小闹的线条,而是一条持续稳定在高位,数值约等于你在 JMeter 聚合报告里看到的吞吐量。​
    • ​响应时间图表:可以清晰地看到在 50 并发下,接口的平均响应时间和 P95/P99 响应时间是多少。​
    • ​JVM 内存图表:会看到一个非常经典的锯齿状图形。这是因为在高并发下,内存使用量快速上升,直到触发垃圾回收(GC),内存瞬间下降,然后再次快速上升,如此往复。​
    • ​CPU 使用率:CPU 将会稳定在一个较高的水平,真实地反映了系统在处理 50 并发用户请求时的计算压力。​
    • ​Tomcat 线程数:活跃线程数会迅速上升到 50 左右,并保持稳定,直观地体现了并发用户的数量。​
  • ​停止压测:观察足够后,点击 JMeter 工具栏的停止(红色的方块)或平滑关闭按钮来结束测试。你会看到 Grafana 上的所有指标都随之回落。​

现在,回到 Grafana 仪表盘 (刷新一下),会看到图表开始跳动,QPS、响应时间、JVM 内存、CPU 使用率……所有信息都一目了然。

为什么仪表盘上没有数据?

这是所有初学者在搭建监控系统时必然会遇到的经典问题。

别担心,这通常不是什么大问题,而是数据流在某个环节被卡住了。通常有如下几个排查思路:

  • Spring Boot 应用 - 负责 产生 指标数据。
  • Prometheus - 负责 抓取并存储 这些数据。
  • Grafana - 负责 查询并展示 这些数据。

只要其中一级工作不正常,最终的结果就是没有数据。现在,我们来一步步地排查问题。

首先检查Spring Boot 应用是否在正常生产数据?

在浏览器或使用 curl 访问 Actuator 暴露的 Prometheus 端点:

curl http://localhost:8080/actuator/prometheus

正常情况 ✅:你应该能看到满屏的、以 # 和文本开头的指标数据,例如 jvm_memory_used_bytes...、http_server_requests_seconds_count... 等等。

异常情况 ❌:连接被拒绝 (Connection Refused):说明 Spring Boot 应用没启动。 404 Not Found:说明 application.yml 配置有误,没有正确暴露 prometheus 端点。


第 2 步:检查 - Prometheus 是否成功抓取到数据?

这是最常见的故障点。Prometheus 是数据中转站,它必须能成功连接到你的应用。

  • 打开 Prometheus UI:在浏览器访问 http://localhost:9090
  • 检查 Targets 页面:点击顶部菜单的 "Status" -> "Targets"。
  • 分析 Target 状态:在 Targets 页面,找到 job_name 为 spring-boot-app 的那一行,看它的 State:
    • State 是 UP (绿色) ✅:这说明 Prometheus 已经成功连接并抓取到了应用的数据。问题几乎可以确定是在 Grafana 那边。
    • State 是 DOWN (红色)❌ :问题就在这里。 这说明 Prometheus 无法连接到 Spring Boot 应用。
      • 网络问题 (90% 的可能性):Prometheus 是在 Docker 容器里运行的,在容器内部,localhost 指的不是你电脑的 localhost,而是它自己。它需要一个特殊的方式来访问宿主机(你的电脑)。
  • 防火墙问题:你电脑的防火墙可能阻止了来自 Docker 容器的访问。可以暂时关闭防火墙测试一下。
  • 配置错误:检查 prometheus.yml 里的端口号(8080)或 metrics_path(/actuator/prometheus)是否写错了。

第 3 步:检查 - Grafana 是否配置正确?

如果你能确认 Prometheus 的 Target 是 UP 状态,那么问题就一定出在 Grafana 的配置上。

  • ​检查数据源连接:

    • 登录 Grafana (**http://localhost:3000**)。

    • 进入左侧菜单的齿轮图标 (Configuration) -> "Data Sources"。

    • 点击 Prometheus 数据源。

    • ​关键检查点:Prometheus server URL配置是否正确?

      • 错误配置 ❌: http://localhost:9090。在 Grafana 容器内部,localhost指的是它自己,而不是 Prometheus 容器。
      • 正确配置 ✅: http://prometheus:9090。因为 Grafana 和 Prometheus 运行在同一个 docker-compose网络中,它们可以直接通过服务名(prometheus)进行通信。​ ​
    • 点击页面底部的 "Save & test" 按钮。你必须看到一个绿色的 "Data source is working" 提示。如果报错,说明 URL 肯定写错了。

  • ​检查仪表盘和查询语句: ​* ​确认查询目标:打开你的仪表盘,随便点开一个没有数据的面板,点击 "Edit"。查看下方的查询语句,例如 sum(rate(http_server_requests_seconds_count[1m]))。

    • ​​去 Prometheus 验证查询:复制这个查询语句,**回到 Prometheus 的 UI 界面 (**http://localhost:9090**)**,将它粘贴到查询框中,点击 "Execute"。

      ​​* ​正常情况 ✅:Prometheus 中应该能查询出数据图表。如果这里有数据,而 Grafana 没有,说明问题出在 Grafana 的数据源连接或时间范围上。​

      • 异常情况 ❌:如果在 Prometheus 里也查不到数据(返回 "Empty query result"),但你的 Target 又是 UP 的,说明这个指标可能还没产生。你是否对你的 Spring Boot 接口发起了请求? http_server_requests_seconds_count 这样的指标,只有在接口被调用后才会产生。
  • 检查时间范围:这是个非常容易忽略的坑。在 Grafana 仪表盘的右上角,有一个时间范围选择器。

    • 确认你查看的时间范围是正确的:比如,应用刚启动 5 分钟,但选择的是 "Last 6 hours",那么图表上自然是空的。
    • ​​解决方案:将时间范围调整为 "Last 5 minutes",并设置一个自动刷新(比如每 5 秒)。 ​​

请按照以上1 -> 2 -> 3的顺序进行排查,不要跳步。99% 的无数据问题都能通过这个流程定位到。

  • 先curl你的 Spring Boot 应用。
  • 再去看 Prometheus 的Targets页面。
  • 最后才去检查 Grafana 的Data Source和Dashboard。

总结

到这里我们已经成功地搭建了一套现代化的应用监控系统。回顾一下,我们完成了:

  • 用 Spring Boot Actuator 和 Micrometer 暴露了应用的核心指标。
  • 用 Prometheus 作为时间序列数据库来收集和存储这些指标。
  • 用 Grafana 进行了可视化展示。

这套技术栈是当前云原生领域监控的基石。从这里开始,我们之后可以继续探索更高级的 PromQL 查询、自定义 Grafana 图表以及告警设置,成为一名全栈监控高手。

Comments