Java Web监控:使用Prometheus与Grafana
在Java Web应用的全生命周期中,监控系统扮演着“健康管家”的角色。随着应用规模扩大和分布式架构普及,传统的日志打印和手动排查已无法满足需求——开发者需要实时掌握系统性能瓶颈、资源使用情况和业务指标波动。Prometheus与Grafana的组合已成为云原生时代监控的事实标准,能够实现指标采集、存储、可视化与告警的全流程管理。
本文将系统讲解如何基于Prometheus与Grafana构建Java Web应用的监控体系,从环境搭建、指标埋点、数据采集到可视化 dashboard 设计,覆盖从基础到进阶的实战技巧,附详细代码示例和配置说明,帮助开发者从零搭建企业级监控平台。
一、监控系统的核心价值与技术选型
监控是保障Java Web应用稳定运行的基石。一套完善的监控系统能在问题爆发前预警、在故障发生时快速定位根因、在优化时提供数据支撑。
1. Java Web监控的核心需求
Java Web应用的监控需覆盖“基础设施-应用性能-业务指标”三个维度,核心需求包括:
- 实时性:关键指标(如CPU使用率、请求延迟)需秒级更新,确保问题及时发现;
- 全面性:覆盖服务器资源(CPU/内存/磁盘)、JVM状态(GC/堆内存)、应用性能(接口响应时间/错误率)、业务指标(订单量/支付成功率);
- 可追溯性:指标数据需长期存储,支持历史对比分析(如“今日峰值与上周同期对比”);
- 可视化:通过图表直观展示指标趋势,替代枯燥的数字罗列;
- 告警能力:当指标超过阈值(如错误率>5%)时,通过邮件、钉钉等渠道自动通知相关人员。
典型痛点:某电商平台在大促期间突然出现订单支付失败,因缺乏实时监控,运维人员花3小时才定位到“数据库连接池耗尽”的问题,导致直接损失超10万元。
2. 技术选型:Prometheus与Grafana的组合优势
在众多监控工具中,Prometheus与Grafana的组合脱颖而出,成为Java Web监控的首选方案。二者的核心优势如下:
工具 | 角色 | 核心优势 |
---|---|---|
Prometheus | 指标采集与存储 | 时序数据模型优化、强大的PromQL查询语言、分布式部署支持、原生告警功能 |
Grafana | 可视化与 dashboard | 丰富的图表类型(折线图/柱状图/热力图等)、拖拽式 dashboard 设计、多数据源支持、告警集成 |
技术栈优势:Prometheus负责从Java应用和服务器中采集指标并存储,通过PromQL实现灵活查询;Grafana连接Prometheus数据源,将指标转化为直观的可视化图表,二者协同形成“采集-存储-查询-可视化-告警”的完整闭环。
3. 监控体系的核心组件
一套完整的Java Web监控体系需包含以下组件:
- 指标埋点工具:在Java应用中嵌入指标采集逻辑,如Micrometer(Java生态的指标门面);
- 暴露器:将应用指标通过HTTP接口暴露,供Prometheus采集(如Prometheus Java Client);
- Prometheus Server:负责定时采集、存储指标数据,提供PromQL查询接口;
- Grafana:连接Prometheus,创建可视化 dashboard;
- 告警管理器:Prometheus Alertmanager负责告警聚合、路由与分发;
- ** exporters**:采集非应用指标(如服务器资源、数据库性能),如Node Exporter(服务器监控)、MySQL Exporter(数据库监控)。
工作流程:
Java应用 → Micrometer埋点 → Prometheus Client暴露指标 → Prometheus Server采集存储 → Grafana可视化 → Alertmanager告警
二、环境搭建:Prometheus与Grafana基础部署
在进行Java应用集成前,需先完成Prometheus与Grafana的基础部署。推荐使用Docker快速搭建环境,降低配置复杂度。
1. 环境准备
- 操作系统:Linux(推荐Ubuntu 20.04+或CentOS 7+)、Windows 10+(WSL2)或macOS;
- 依赖软件:Docker 20.10+、Docker Compose 2.0+;
- 硬件要求:至少2GB内存(Prometheus和Grafana本身消耗较低,主要取决于监控数据量)。
2. Docker Compose部署Prometheus与Grafana
使用Docker Compose可一键启动Prometheus、Grafana和Node Exporter(用于服务器监控),配置如下:
(1)创建目录结构
mkdir -p prometheus-grafana/{prometheus, grafana, data/prometheus, data/grafana}
cd prometheus-grafana
(2)编写Prometheus配置文件
创建prometheus/prometheus.yml
:
global:
scrape_interval: 15s # 全局采集间隔(默认15秒)
evaluation_interval: 15s # 告警规则评估间隔
# 告警规则文件配置
rule_files:
- "alert_rules.yml" # 后续会创建告警规则
# 采集目标配置
scrape_configs:
# 监控Prometheus自身
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
# 监控Grafana
- job_name: "grafana"
static_configs:
- targets: ["grafana:3000"]
# 监控服务器资源(Node Exporter)
- job_name: "node"
static_configs:
- targets: ["node-exporter:9100"]
# 监控Java应用(后续会添加应用地址)
- job_name: "java-web-app"
metrics_path: "/actuator/prometheus" # Spring Boot应用的指标暴露路径
static_configs:
- targets: ["host.docker.internal:8080"] # 本地Java应用地址(Windows/macOS)
# 若为Linux,替换为实际IP:- targets: ["192.168.1.100:8080"]
(3)编写告警规则文件
创建prometheus/alert_rules.yml
,定义基础告警规则:
groups:
- name: java-web-app-alerts
rules:
# 应用不可用告警
- alert: AppDown
expr: up{job="java-web-app"} == 0
for: 1m # 持续1分钟符合条件才告警
labels:
severity: critical # 告警级别
annotations:
summary: "Java应用不可用"
description: "应用{{ $labels.instance }}已下线超过1分钟"
# 高错误率告警
- alert: HighErrorRate
expr: sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_count[5m])) > 0.05
for: 2m
labels:
severity: warning
annotations:
summary: "应用错误率过高"
description: "过去5分钟错误率{{ $value | humanizePercentage }},超过5%阈值"
# JVM堆内存使用率过高
- alert: HighJvmHeapUsage
expr: jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "JVM堆内存使用率过高"
description: "堆内存使用率{{ $value | humanizePercentage }},超过80%阈值"
(4)编写Docker Compose配置
创建docker-compose.yml
:
version: '3.8'
services:
# Prometheus服务
prometheus:
image: prom/prometheus:v2.45.0
container_name: java-monitor-prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/alert_rules.yml:/etc/prometheus/alert_rules.yml
- ./data/prometheus:/prometheus # 数据持久化
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle' # 支持热加载配置
restart: always
networks:
- monitor-network
# Grafana服务
grafana:
image: grafana/grafana:9.5.2
container_name: java-monitor-grafana
volumes:
- ./data/grafana:/var/lib/grafana # 数据持久化
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin # 初始密码
- GF_USERS_ALLOW_SIGN_UP=false # 禁用注册
restart: always
depends_on:
- prometheus
networks:
- monitor-network
# Node Exporter:采集服务器资源指标
node-exporter:
image: prom/node-exporter:v1.6.1
container_name: java-monitor-node-exporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
ports:
- "9100:9100"
restart: always
networks:
- monitor-network
# Alertmanager:处理告警
alertmanager:
image: prom/alertmanager:v0.25.0
container_name: java-monitor-alertmanager
volumes:
- ./prometheus/alertmanager.yml:/etc/alertmanager/alertmanager.yml
- ./data/alertmanager:/data
ports:
- "9093:9093"
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/data'
restart: always
depends_on:
- prometheus
networks:
- monitor-network
networks:
monitor-network:
driver: bridge
(5)编写Alertmanager配置
创建prometheus/alertmanager.yml
,配置告警接收方式(以邮件为例):
global:
resolve_timeout: 5m # 告警解决后等待5分钟确认
route:
group_by: ['alertname', 'job'] # 按告警名称和job分组
group_wait: 10s # 组内第一个告警等待10秒,可能合并同类告警
group_interval: 10s # 组内告警间隔10秒发送
repeat_interval: 1h # 重复告警间隔1小时
receiver: 'email-notifications' # 默认接收者
receivers:
- name: 'email-notifications'
email_configs:
- to: 'admin@example.com' # 接收告警的邮箱
from: 'prometheus@example.com'
smarthost: 'smtp.example.com:587' # SMTP服务器地址和端口
auth_username: 'prometheus@example.com'
auth_password: 'your-email-password' # 邮箱密码或授权码
auth_identity: 'prometheus@example.com'
send_resolved: true # 告警解决后发送恢复通知
inhibit_rules: # 抑制规则(高优先级告警触发时,抑制低优先级告警)
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'job', 'instance']
(6)启动服务
# 启动所有服务
docker-compose up -d
# 查看启动状态
docker-compose ps
服务启动后,可通过以下地址访问:
- Prometheus:
http://localhost:9090
(Web UI) - Grafana:
http://localhost:3000
(默认账号密码:admin/admin) - Node Exporter:
http://localhost:9100/metrics
(服务器指标) - Alertmanager:
http://localhost:9093
(告警管理)
3. 验证基础监控是否正常
(1)验证Prometheus采集状态
访问Prometheus Web UI(http://localhost:9090
),进入“Status → Targets”页面,确认所有job的“State”均为“UP”:
prometheus
:Prometheus自身监控grafana
:Grafana监控node
:Node Exporter监控(服务器资源)
(2)验证指标查询功能
在Prometheus的“Graph”页面,输入以下PromQL查询指标,验证是否有数据返回:
- 服务器CPU使用率:
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
- Prometheus自身内存使用:
prometheus_tsdb_head_series
(当前存储的时间序列数)
(3)验证Grafana基础功能
访问Grafana(http://localhost:3000
),首次登录需修改密码(建议改为grafana123
方便测试)。进入“Home”页面,确认界面正常加载。
三、Java Web应用集成Prometheus:指标埋点与暴露
Java Web应用需通过埋点暴露关键指标(如接口响应时间、JVM状态),供Prometheus采集。Spring Boot应用可通过Micrometer快速集成,非Spring应用可使用Prometheus Java Client。
1. Spring Boot应用集成(推荐)
Spring Boot 2.x+默认集成Micrometer作为指标门面,可通过spring-boot-starter-actuator
快速暴露指标。
(1)添加依赖
在pom.xml
中添加以下依赖:
<!-- Spring Boot Actuator:提供监控端点 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer Prometheus注册表:将指标转换为Prometheus格式 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- Spring Boot Web:确保应用是Web类型 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(2)配置application.yml
在src/main/resources/application.yml
中配置Actuator与Prometheus:
spring:
application:
name: java-web-app # 应用名称,将作为指标标签
# Actuator配置
management:
endpoints:
web:
exposure:
include: health,prometheus,info # 暴露的端点,必须包含prometheus
base-path: /actuator # 端点基础路径(默认/actuator)
metrics:
export:
prometheus:
enabled: true # 启用Prometheus导出
tags:
application: ${spring.application.name} # 所有指标添加application标签
endpoint:
health:
show-details: always # 健康检查显示详细信息
metrics:
enabled: true
prometheus:
enabled: true
(3)启动Spring Boot应用
创建简单的Spring Boot启动类和控制器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class JavaWebAppApplication {
public static void main(String[] args) {
SpringApplication.run(JavaWebAppApplication.class, args);
}
// 测试接口:模拟用户查询
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
// 模拟处理时间(随机延迟0-200ms)
try {
Thread.sleep((long) (Math.random() * 200));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "User: " + id;
}
// 测试接口:模拟订单创建
@GetMapping("/order")
public String createOrder() {
// 模拟偶尔失败(10%概率)
if (Math.random() < 0.1) {
throw new RuntimeException("创建订单失败");
}
return "Order created";
}
}
启动应用后,访问http://localhost:8080/actuator/prometheus
,应能看到暴露的Prometheus格式指标,例如:
# HELP http_server_requests_seconds Duration of HTTP server requests
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{application="java-web-app",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/user/{id}",} 10.0
http_server_requests_seconds_sum{application="java-web-app",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/user/{id}",} 1.234
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{application="java-web-app",area="heap",} 1.23456789E8
(4)验证Prometheus采集Java应用指标
回到Prometheus Web UI(http://localhost:9090
),进入“Status → Targets”,确认java-web-app
的State为“UP”。在“Graph”页面查询Java应用指标:
- 接口请求数:
http_server_requests_seconds_count{application="java-web-app"}
- JVM堆内存使用:
jvm_memory_used_bytes{application="java-web-app", area="heap"}
2. 自定义指标埋点:业务与性能指标
除了Actuator自动暴露的指标,还需针对业务场景和关键流程自定义指标,如订单量、支付成功率、自定义耗时等。
(1)Micrometer核心指标类型
Micrometer支持多种指标类型,覆盖不同监控场景:
指标类型 | 作用 | 适用场景 |
---|---|---|
Counter | 单调递增的计数器 | 接口请求数、订单创建数、错误数 |
Gauge | 可增可减的仪表盘 | 在线用户数、队列长度、缓存命中率 |
Timer | 记录耗时与频率 | 接口响应时间、方法执行时间 |
Summary | 记录分布统计(均值、分位数) | 延迟分布、数据大小分布 |
Histogram | 记录直方图分布(需配合Prometheus的histogram_quantile) | 精确分位数计算(如P95延迟) |
(2)自定义指标实战示例
创建一个指标服务类,封装常用自定义指标:
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.binder.MeterBinder;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class CustomMetrics implements MeterBinder {
// 订单相关指标
private Counter orderCreateCounter; // 订单创建计数器
private Counter orderPayCounter; // 订单支付计数器
private Timer orderProcessTimer; // 订单处理计时器
// 在线用户数指标
private final AtomicInteger onlineUserCount = new AtomicInteger(0);
// 商品库存指标(模拟)
private final Map<String, AtomicInteger> productStock = new HashMap<>();
@PostConstruct
public void init() {
// 初始化商品库存(模拟数据)
productStock.put("product-1", new AtomicInteger(100));
productStock.put("product-2", new AtomicInteger(50));
}
// 绑定指标到注册中心
@Override
public void bindTo(io.micrometer.core.instrument.MeterRegistry registry) {
// 1. 订单创建计数器
orderCreateCounter = Counter.builder("business.order.create.count")
.description("订单创建总数")
.tag("app", "java-web-app")
.register(registry);
// 2. 订单支付计数器
orderPayCounter = Counter.builder("business.order.pay.count")
.description("订单支付总数")
.tag("app", "java-web-app")
.register(registry);
// 3. 订单处理计时器
orderProcessTimer = Timer.builder("business.order.process.time")
.description("订单处理耗时")
.tag("app", "java-web-app")
.register(registry);
// 4. 在线用户数Gauge
Gauge.builder("business.user.online.count", onlineUserCount, AtomicInteger::get)
.description("当前在线用户数")
.tag("app", "java-web-app")
.register(registry);
// 5. 商品库存Gauge(动态标签)
for (Map.Entry<String, AtomicInteger> entry : productStock.entrySet()) {
String productId = entry.getKey();
AtomicInteger stock = entry.getValue();
Gauge.builder("business.product.stock.count", stock, AtomicInteger::get)
.description("商品库存数量")
.tag("app", "java-web-app")
.tag("product_id", productId)
.register(registry);
}
}
// 订单创建计数
public void incrementOrderCreate() {
orderCreateCounter.increment();
}
// 订单支付计数
public void incrementOrderPay() {
orderPayCounter.increment();
}
// 记录订单处理时间(函数式用法)
public <T> T recordOrderProcessTime(Supplier<T> task) {
return orderProcessTimer.record(task);
}
// 在线用户数+1
public void incrementOnlineUser() {
onlineUserCount.incrementAndGet();
}
// 在线用户数-1
public void decrementOnlineUser() {
onlineUserCount.decrementAndGet();
}
// 减少商品库存
public void decreaseStock(String productId, int amount) {
AtomicInteger stock = productStock.get(productId);
if (stock != null) {
stock.addAndGet(-amount);
}
}
}
(3)在业务代码中使用自定义指标
修改控制器,集成自定义指标:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
public class BusinessController {
@Autowired
private CustomMetrics customMetrics;
// 订单创建接口
@PostMapping("/order")
public String createOrder(@RequestParam String productId) {
// 记录订单创建
customMetrics.incrementOrderCreate();
// 记录订单处理时间
return customMetrics.recordOrderProcessTime(() -> {
// 模拟订单处理(50-300ms)
try {
Thread.sleep(50 + (long) (Math.random() * 250));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 减少库存
customMetrics.decreaseStock(productId, 1);
return "Order created for product: " + productId;
});
}
// 订单支付接口
@PostMapping("/order/{id}/pay")
public String payOrder(@PathVariable Long id) {
// 模拟支付处理
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 记录支付成功
customMetrics.incrementOrderPay();
return "Order " + id + " paid";
}
// 用户登录接口(更新在线人数)
@PostMapping("/user/login")
public String login() {
customMetrics.incrementOnlineUser();
return "Login success";
}
// 用户登出接口(更新在线人数)
@PostMapping("/user/logout")
public String logout() {
customMetrics.decrementOnlineUser();
return "Logout success";
}
}
启动应用后,调用几次接口(如POST /order?productId=product-1
、POST /user/login
),再访问http://localhost:8080/actuator/prometheus
,应能看到自定义指标:
# HELP business_order_create_count 订单创建总数
# TYPE business_order_create_count counter
business_order_create_count{app="java-web-app",} 5.0
# HELP business_user_online_count 当前在线用户数
# TYPE business_user_online_count gauge
business_user_online_count{app="java-web-app",} 3.0
# HELP business_product_stock_count 商品库存数量
# TYPE business_product_stock_count gauge
business_product_stock_count{app="java-web-app",product_id="product-1",} 95.0
3. 非Spring Boot应用集成(原生Java)
非Spring Boot应用(如传统Servlet应用)可通过Prometheus Java Client直接暴露指标。
(1)添加依赖
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
<version>0.16.0</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_servlet</artifactId>
<version>0.16.0</version>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId> <!-- JVM指标 -->
<version>0.16.0</version>
</dependency>
(2)初始化指标与Servlet
创建指标注册类:
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;
import io.prometheus.client.exporter.MetricsServlet;
import io.prometheus.client.hotspot.DefaultExports;
import javax.servlet.annotation.WebServlet;
// 注册Prometheus指标Servlet
@WebServlet("/metrics")
public class PrometheusMetricsServlet extends MetricsServlet {
static {
// 注册JVM指标
DefaultExports.initialize();
}
// 接口请求计数器
public static final Counter requestCounter = Counter.build()
.name("http_requests_total")
.help("Total HTTP requests")
.labelNames("method", "path", "status")
.register();
// 接口响应时间直方图
public static final Histogram requestDuration = Histogram.build()
.name("http_request_duration_seconds")
.help("HTTP request duration in seconds")
.labelNames("method", "path")
.register();
// 在线用户数
public static final Gauge onlineUsers = Gauge.build()
.name("online_users_total")
.help("Current online users")
.register();
}
(3)在Servlet中使用指标
import io.prometheus.client.Histogram.Timer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String path = "/user";
String method = "GET";
Timer timer = PrometheusMetricsServlet.requestDuration.labels(method, path).startTimer();
try {
// 模拟处理逻辑
Thread.sleep((long) (Math.random() * 200));
resp.getWriter().println("User info");
PrometheusMetricsServlet.requestCounter.labels(method, path, "200").inc();
} catch (Exception e) {
resp.sendError(500, "Error");
PrometheusMetricsServlet.requestCounter.labels(method, path, "500").inc();
} finally {
timer.observeDuration(); // 记录耗时
}
}
}
部署应用后,访问http://localhost:8080/metrics
即可获取指标,Prometheus配置类似Spring Boot应用。
四、Prometheus核心配置与PromQL查询
Prometheus的强大之处在于灵活的配置和强大的查询语言PromQL。掌握这些技能是实现精准监控的基础。
1. Prometheus核心配置详解
Prometheus的配置文件(prometheus.yml
)包含全局配置、告警规则和采集目标,以下是关键参数详解:
(1)全局配置(global)
global:
scrape_interval: 15s # 所有采集目标的默认采集间隔
evaluation_interval: 15s # 告警规则的评估间隔
scrape_timeout: 10s # 单个采集请求的超时时间
external_labels: # 附加到所有时序数据的标签(用于联邦部署)
monitor: "java-web-monitor"
(2)采集配置(scrape_configs)
每个job_name
定义一组采集目标:
scrape_configs:
- job_name: "java-web-app" # 任务名称(会作为标签添加到指标)
scrape_interval: 5s # 覆盖全局采集间隔(高频指标可设为5s)
metrics_path: "/actuator/prometheus" # 指标路径
scheme: "http" # 协议(http/https)
basic_auth: # 若应用有认证,配置账号密码
username: "admin"
password: "secret"
static_configs: # 静态配置采集目标
- targets: ["192.168.1.100:8080", "192.168.1.101:8080"] # 多个实例
labels: # 附加标签(如环境、机房)
env: "prod"
机房: "北京"
relabel_configs: # 标签重写规则(高级功能)
- source_labels: [__address__] # 源标签(__address__是内置标签)
regex: "(.*):8080" # 正则匹配
target_label: instance # 目标标签
replacement: "${1}" # 替换为IP(去除端口)
(3)服务发现配置(动态目标)
分布式环境中,静态配置难以维护,可使用服务发现(如Kubernetes、Consul):
# Kubernetes服务发现示例
- job_name: "k8s-java-app"
kubernetes_sd_configs:
- role: pod # 发现Pod
relabel_configs:
# 仅采集带有"app=java-web-app"标签的Pod
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: java-web-app
# 从Pod注解中获取指标路径
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
2. PromQL核心语法与常用查询
PromQL(Prometheus Query Language)是用于查询指标数据的函数式语言,支持聚合、过滤和数学运算。
(1)基础查询
- 直接查询指标:
http_server_requests_seconds_count
(返回该指标的所有时间序列) - 按标签过滤:
http_server_requests_seconds_count{status="200", uri="/user/{id}"}
(只返回状态200且路径为/user/{id}
的序列) - 范围查询:
http_server_requests_seconds_count{uri="/order"}[5m]
(返回过去5分钟的所有样本)
(2)常用函数
函数 | 作用 | 示例 |
---|---|---|
rate() | 计算每秒增长率(适合计数器) | rate(http_requests_total[5m]) (5分钟窗口内的每秒请求数) |
irate() | 计算瞬时增长率(更灵敏,适合高频变化指标) | irate(node_cpu_seconds_total{mode!="idle"}[5m]) |
sum() | 按标签聚合求和 | sum(rate(http_requests_total[5m])) by (status) (按状态聚合请求率) |
avg() | 计算平均值 | avg(rate(http_requests_seconds_sum[5m]) / rate(http_requests_seconds_count[5m])) by (uri) (按路径计算平均响应时间) |
topk() | 取前N个值 | topk(3, sum(rate(http_requests_total[5m])) by (uri)) (请求量前三的接口) |
histogram_quantile() | 计算分位数 | histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, uri)) (各接口的P95延迟) |
(3)Java Web常用指标查询
监控目标 | PromQL查询 | 含义 |
---|---|---|
接口QPS | sum(rate(http_server_requests_seconds_count[5m])) by (uri) | 各接口5分钟内的每秒请求数 |
接口错误率 | sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (uri) / sum(rate(http_server_requests_seconds_count[5m])) by (uri) | 各接口的5xx错误率 |
平均响应时间 | sum(rate(http_server_requests_seconds_sum[5m])) by (uri) / sum(rate(http_server_requests_seconds_count[5m])) by (uri) | 各接口的平均响应时间(秒) |
P95响应时间 | histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[5m])) by (le, uri)) | 95%的请求响应时间不超过该值 |
JVM堆内存使用率 | jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} * 100 | 堆内存使用率(百分比) |
GC次数 | sum(rate(jvm_gc_pause_seconds_count[5m])) by (gc) | 各GC类型的每秒发生次数 |
在线用户数 | business_user_online_count{app="java-web-app"} | 当前在线用户数 |
订单创建速率 | rate(business_order_create_count[5m]) | 每秒订单创建数 |
3. 告警规则配置进阶
Prometheus的告警规则定义在alert_rules.yml
中,除基础阈值告警外,还支持复杂逻辑告警。
(1)多条件组合告警
# 接口响应时间与错误率联合告警
- alert: CriticalApiIssue
expr: |
(sum(rate(http_server_requests_seconds_sum[5m]) by (uri) / rate(http_server_requests_seconds_count[5m]) by (uri)) > 0.5)
and
(sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]) by (uri)) > 1)
for: 2m
labels:
severity: critical
annotations:
summary: "接口{{ $labels.uri }}异常"
description: "接口{{ $labels.uri }}响应时间>500ms且错误率>1次/秒,持续2分钟"
(2)环比异常告警
# 订单量突降告警(与上周同期相比下降50%以上)
- alert: OrderVolumeDrop
expr: |
rate(business_order_create_count[30m]) <
(rate(business_order_create_count[30m] offset 1w) * 0.5)
for: 1h
labels:
severity: warning
annotations:
summary: "订单量异常下降"
description: "当前订单量{{ $value | humanize }}/秒,较上周同期下降超过50%"
(3)告警抑制与分组
通过Alertmanager的配置实现告警聚合,避免告警风暴:
- 分组(group_by):将同类型告警合并发送(如同一应用的多个接口错误);
- 抑制(inhibit_rules):高优先级告警触发时,抑制低优先级告警(如应用宕机时,不再发送接口错误告警);
- 路由(route):按标签将告警发送到不同接收者(如生产环境告警给运维,测试环境告警给开发)。
五、Grafana可视化:从数据源到Dashboard
Grafana是监控数据的“可视化引擎”,能将Prometheus的时序数据转化为直观的图表和dashboard。以下是从零构建Java Web监控dashboard的详细步骤。
1. 添加Prometheus数据源
首次使用Grafana需先配置数据源:
- 登录Grafana(
http://localhost:3000
),进入左侧菜单 Configuration → Data Sources; - 点击“Add data source”,选择“Prometheus”;
- 在配置页面:
- Name:输入“Prometheus-Java-Monitor”;
- URL:输入Prometheus地址(
http://prometheus:9090
,Docker内部通信); - 其他配置保持默认,点击“Save & test”,显示“Data source is working”即配置成功。
2. 创建基础监控Dashboard
Dashboard由多个面板(Panel)组成,每个面板对应一个或多个指标的可视化图表。
(1)创建JVM监控面板
-
进入 Dashboards → New dashboard → Add visualization;
-
选择数据源“Prometheus-Java-Monitor”;
-
配置面板:
- Panel title:输入“JVM堆内存使用率”;
- Query:输入PromQL
jvm_memory_used_bytes{application="java-web-app", area="heap"} / jvm_memory_max_bytes{application="java-web-app", area="heap"} * 100
; - Legend:设置为
{{ instance }}
(按实例显示图例); - Visualization:选择“Gauge”(仪表盘),设置阈值(如80%为警告,90%为错误);
- 点击右上角“Apply”保存面板。
-
新增JVM非堆内存面板,类似配置,指标改为
area="nonheap"
。
(2)创建接口性能面板
-
点击Dashboard右上角“Add” → “Visualization”;
-
配置“接口QPS”面板:
- Query:
sum(rate(http_server_requests_seconds_count{application="java-web-app"}[5m])) by (uri)
; - Visualization:选择“Graph”(折线图);
- X-Axis:设置为“Time”;
- Y-Axis:标题“QPS”;
- Legend:
{{ uri }}
。
- Query:
-
配置“接口平均响应时间”面板:
- Query:
sum(rate(http_server_requests_seconds_sum{application="java-web-app"}[5m])) by (uri) / sum(rate(http_server_requests_seconds_count{application="java-web-app"}[5m])) by (uri)
; - Y-Axis:标题“平均响应时间(秒)”;
- Unit:选择“seconds”。
- Query:
-
配置“接口错误率”面板:
- Query:
sum(rate(http_server_requests_seconds_count{application="java-web-app", status=~"5.."}[5m])) by (uri) / sum(rate(http_server_requests_seconds_count{application="java-web-app"}[5m])) by (uri) * 100
; - Y-Axis:标题“错误率(%)”;
- Unit:选择“percent (0-100)”;
- 设置阈值线(如5%红线)。
- Query:
(3)创建业务指标面板
-
新增“订单创建速率”面板:
- Query:
rate(business_order_create_count{app="java-web-app"}[5m])
; - Visualization:选择“Graph”,Y轴标题“订单/秒”。
- Query:
-
新增“在线用户数”面板:
- Query:
business_user_online_count{app="java-web-app"}
; - Visualization:选择“Stat”(大数字),突出显示当前值。
- Query:
-
新增“商品库存”面板:
- Query:
business_product_stock_count{app="java-web-app"}
; - Visualization:选择“Table”(表格),显示
product_id
和库存值。
- Query:
3. 导入社区Dashboard模板
Grafana社区提供大量现成的Java监控模板,可直接导入使用,节省配置时间。
(1)导入JVM监控模板
- 进入 Dashboards → New dashboard → Import;
- 在“Import via grafana.com”输入模板ID:
4701
(Spring Boot应用监控模板); - 点击“Load”,选择数据源为“Prometheus-Java-Monitor”;
- 点击“Import”完成导入,该模板包含JVM内存、GC、线程等全面监控图表。
(2)导入服务器监控模板
- 同样方法导入模板ID:
1860
(Node Exporter服务器监控); - 选择数据源为“Prometheus-Java-Monitor”,完成导入,可监控CPU、内存、磁盘、网络等服务器指标。
4. Dashboard优化与最佳实践
(1)布局与分组
- 按监控维度分组面板(如“JVM监控”“接口性能”“业务指标”“服务器资源”);
- 重要指标(如错误率、内存使用率)放在顶部或左侧,优先展示;
- 调整面板大小,避免过大或过小(关键指标可设为“Stat”大面板)。
(2)时间范围与刷新频率
- 根据监控目标设置时间范围:实时监控用“Last 1h”,趋势分析用“Last 7d”;
- 刷新频率:高频指标(如QPS)设为“5s”,低频指标(如内存)设为“30s”;
- 在Dashboard设置中配置默认时间范围和刷新频率。
(3)变量与模板化
通过变量实现Dashboard动态切换(如多环境、多实例):
- 进入Dashboard设置 → “Variables” → “Add variable”;
- 配置“instance”变量:
- Name:
instance
; - Type:
Query
; - Data source:选择Prometheus;
- Query:
label_values(http_server_requests_seconds_count{application="java-web-app"}, instance)
; - Sort:
Alphabetical (asc)
;
- Name:
- 在面板Query中使用变量:
http_server_requests_seconds_count{instance=~"$instance"}
,实现通过下拉框切换实例。
六、进阶监控:分布式追踪与日志关联
在分布式Java Web应用中,单一指标监控难以定位跨服务问题,需结合分布式追踪和日志关联,构建“指标-追踪-日志”三位一体的监控体系。
1. 集成分布式追踪(Spring Cloud Sleuth + Zipkin)
Spring Cloud Sleuth可生成TraceID追踪跨服务请求,与Prometheus指标结合实现全链路监控。
(1)添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
(2)配置application.yml
spring:
sleuth:
sampler:
probability: 1.0 # 开发环境采样率100%,生产环境可设0.1
zipkin:
base-url: http://localhost:9411 # Zipkin服务器地址
(3)启动Zipkin
docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin
(4)追踪指标关联
通过MDC将TraceID添加到日志,再通过Prometheus的traceId
标签关联指标与追踪:
import brave.Tracer;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class TraceIdFilter extends OncePerRequestFilter {
@Autowired
private Tracer tracer;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 将Sleuth的TraceID添加到MDC
String traceId = tracer.currentSpan().context().traceIdString();
MDC.put("traceId", traceId);
try {
filterChain.doFilter(request, response);
} finally {
MDC.remove("traceId");
}
}
}
在Grafana中添加TraceID查询链接,实现从指标到追踪的跳转。
2. 日志与指标关联
通过Logback将指标标签(如traceId
、uri
)写入日志,再通过ELK或Grafana Loki实现日志与指标的联动查询。
(1)Logback配置添加指标标签
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [traceId=%X{traceId}] - %msg%n</pattern>
</encoder>
</appender>
(2)集成Grafana Loki收集日志
- 启动Loki和Promtail(日志收集):
# docker-compose.yml中添加
services:
loki:
image: grafana/loki:2.8.0
ports:
- "3100:3100"
volumes:
- ./data/loki:/loki
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.8.0
volumes:
- ./data/promtail:/var/log
- ./promtail/config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
- 在Grafana中添加Loki数据源,实现日志与指标在同一dashboard展示。
七、实战案例:Java Web应用性能问题排查
结合前文知识,通过一个实际案例展示如何用Prometheus与Grafana定位Java Web应用性能问题。
1. 问题场景
某Java Web应用上线后,用户反馈“部分接口响应缓慢”,开发团队需快速定位根因。
2. 排查步骤
(1)查看关键指标dashboard
- 访问Grafana dashboard,发现
/order
接口的平均响应时间从正常的100ms突增至800ms; - 错误率无明显变化(状态码200),但QPS正常(约50 req/s);
- JVM监控面板显示:堆内存使用率达90%,GC次数频繁(每秒Young GC 5次)。
(2)深入分析JVM指标
-
在Prometheus中查询GC指标:
- Young GC次数:
sum(rate(jvm_gc_pause_seconds_count{gc="YoungGC"}[5m])) by (instance)
→ 确认每秒5次; - Young GC耗时:
sum(rate(jvm_gc_pause_seconds_sum{gc="YoungGC"}[5m])) by (instance)
→ 每次GC耗时约50ms; - 结论:频繁GC导致CPU资源消耗,接口响应延迟。
- Young GC次数:
-
分析内存泄漏嫌疑:
- 老年代内存增长:
jvm_memory_used_bytes{area="heap", generation="old"}
→ 持续增长,无下降趋势; - 线程数:
jvm_threads_live_threads
→ 正常(约50); - 怀疑存在内存泄漏对象。
- 老年代内存增长:
(3)结合分布式追踪定位问题接口
- 在Zipkin中查询
/order
接口的慢请求TraceID; - 发现慢请求均涉及
OrderService.calculateDiscount()
方法,耗时约700ms; - 查看该方法代码,发现存在大对象创建未释放的问题:
// 问题代码:每次调用创建大集合,未及时回收
public double calculateDiscount(Order order) {
List<OrderItem> items = new ArrayList<>();
// 循环添加大量元素(10万+)
for (int i = 0; i < order.getItemsCount(); i++) {
items.add(new OrderItem(...));
}
// 业务逻辑处理...
return discount; // 未清空集合,导致内存泄漏
}
(4)验证优化效果
- 修改代码:使用后清空集合或改用局部变量池;
- 上线后观察dashboard:
/order
接口响应时间恢复至120ms;- Young GC次数降至每秒0.5次;
- 堆内存使用率稳定在60%;
- 问题解决。
八、常见问题与最佳实践
1. 常见问题排查
(1)指标不显示或采集失败?
- 检查应用是否正常暴露指标:访问
http://<host>:<port>/actuator/prometheus
确认有数据; - 检查Prometheus Targets状态:
http://localhost:9090/targets
,查看是否有“DOWN”状态及错误原因(如网络不通、认证失败); - 检查指标名称是否正确:PromQL区分大小写,确保标签匹配(如
application="java-web-app"
是否正确); - 检查采集间隔:若指标变化频率低,需等待
scrape_interval
时间后才会显示。
(2)Grafana图表无数据?
- 检查数据源配置:确认Prometheus地址正确,测试连接是否成功;
- 检查时间范围:默认时间范围可能未包含数据,尝试选择“Last 7 days”;
- 检查PromQL语法:在Grafana的Query编辑器中点击“Test query”,查看是否有语法错误;
- 检查指标是否存在:在Prometheus Web UI中先验证Query是否有数据。
(3)告警不触发或未收到通知?
- 检查Prometheus告警规则:
http://localhost:9090/alerts
,查看告警是否处于“FIRING”状态; - 检查Alertmanager配置:
http://localhost:9093
,确认告警是否被正确路由; - 检查通知渠道:如邮件告警,查看Alertmanager日志是否有发送失败(
docker logs java-monitor-alertmanager
); - 检查
for
参数:告警规则的for
设置过长可能导致延迟触发(如for: 5m
需等待5分钟)。
2. 监控最佳实践
(1)指标设计原则
- 核心指标优先:聚焦“4个黄金信号”——延迟(Latency)、流量(Traffic)、错误率(Errors)、饱和度(Saturation);
- 标签设计合理:关键标签(如
application
、instance
、uri
)保持一致,避免过多标签导致 cardinality爆炸; - 避免过度采集:非关键指标可降低采集频率(如30s),减少Prometheus存储压力;
- 业务指标与技术指标结合:技术指标(JVM/CPU)反映系统健康,业务指标(订单量/支付率)反映业务健康。
(2)性能优化建议
- Prometheus存储优化:设置
--storage.tsdb.retention.time=15d
(保留15天数据),避免磁盘空间不足;对高频指标使用rate()
而非原始计数器; - 指标采样策略:生产环境分布式追踪采样率设为0.1(10%),避免性能损耗;
- JVM指标采集优化:使用
micrometer-registry-prometheus
的cacheMetrics
功能,减少重复计算; - 水平扩展:当监控目标超过100个,考虑Prometheus联邦部署(Federation)或使用Thanos扩展存储。
(3)告警策略设计
- 告警分级:按严重程度分为
critical
(核心业务中断)、warning
(性能下降)、info
(非关键提示); - 告警聚合:通过Alertmanager的
group_by
合并同类告警,避免“告警风暴”; - 告警抑制:高优先级告警触发时,抑制低优先级告警(如应用宕机时不告警接口错误);
- 告警自愈:结合自动化工具(如Ansible、Kubernetes HPA),实现简单告警的自动恢复(如扩容实例、重启服务)。
九、总结
Java Web监控是保障应用稳定运行的核心手段,Prometheus与Grafana的组合提供了从指标采集到可视化的完整解决方案。通过本文的实战指南,开发者可从零搭建包含JVM监控、接口性能、业务指标和服务器资源的全方位监控体系。
核心要点包括:
- 利用Micrometer在Java应用中埋点,通过Actuator暴露指标;
- 配置Prometheus采集指标,使用PromQL实现灵活查询;
- 在Grafana中创建直观的dashboard,实现指标可视化;
- 设计合理的告警规则,通过Alertmanager及时通知异常;
- 结合分布式追踪和日志,构建“指标-追踪-日志”联动的排查体系。
随着Java Web应用向分布式、云原生演进,监控系统的重要性将愈发凸显。掌握Prometheus与Grafana的使用技巧,不仅能提升问题排查效率,更能通过数据驱动优化应用性能,为用户提供更稳定的服务体验。建议在实际项目中持续迭代监控策略,让监控系统真正成为开发和运维的“千里眼”与“顺风耳”。