【 Spring Boot Actuator 与可观测性实战】

Spring Boot Actuator 与可观测性实战:从健康检查到 TraceId

线上出问题时,最怕两件事:一是不知道服务还健不健康,二是日志里找不到同一条请求的完整链路。Spring Boot Actuator、Micrometer 与 MDC 恰好补上这两块缺口。本文用一套可落地的配置,把健康检查、指标导出和请求追踪串起来。


为什么需要可观测性

应用一旦部署到多实例环境,仅靠「进程还在」远远不够。数据库连接池打满、Redis 抖动、消息中间件不可达,都可能让接口大面积失败,而进程本身仍在运行。

可观测性通常拆成三块:

维度回答的问题常见手段
健康检查现在能不能接流量Actuator /health
指标哪里变慢、哪里变满Micrometer + Prometheus
日志追踪这次请求经过了哪些节点MDC 中的 traceId

下面按接入顺序实战。


一、引入 Actuator

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Gradle:

implementation 'org.springframework.boot:spring-boot-starter-actuator'

启动后默认会暴露少量端点。生产环境不要一上来把所有端点全部放开,先从 healthinfometrics 开始。


二、暴露核心端点

application.yml 示例:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: when_authorized   # 开发可改为 always
  info:
    env:
      enabled: true
  metrics:
    tags:
      application: ${spring.application.name}

常用端点说明:

端点用途
/actuator/health聚合健康状态,供 K8s / 负载均衡探活
/actuator/info应用元信息(版本、构建号等)
/actuator/metrics指标目录与单个指标查询
/actuator/prometheusPrometheus 抓取格式(需额外依赖)

安全建议:

  1. 生产环境用 Spring Security 限制 /actuator/**
  2. 探活只暴露 health,指标走内网或独立端口
  3. show-details 勿对公网匿名返回依赖细节

可用独立管理端口,与业务端口隔离:

management:
  server:
    port: 8081

三、自定义 Health Indicator

Actuator 对 DataSource、Redis、RabbitMQ 等常见中间件通常自带检查。一旦依赖不在默认列表里,或需要更细的业务判定,就要写自定义 Indicator。

1. 通用自定义示例

以「外部向量检索服务」为例(也可换成任意 HTTP / gRPC 依赖):

@Component
public class VectorStoreHealthIndicator implements HealthIndicator {

    private final VectorStoreClient client;

    public VectorStoreHealthIndicator(VectorStoreClient client) {
        this.client = client;
    }

    @Override
    public Health health() {
        try {
            long latencyMs = client.ping();
            return Health.up()
                    .withDetail("latencyMs", latencyMs)
                    .build();
        } catch (Exception ex) {
            return Health.down(ex)
                    .withDetail("reason", "vector store unreachable")
                    .build();
        }
    }
}

2. Redis 业务级检查

默认 Redis Health 只能证明「能连上」。若还要确认关键 Key 可读:

@Component
public class RedisBizHealthIndicator implements HealthIndicator {

    private final StringRedisTemplate redis;

    public RedisBizHealthIndicator(StringRedisTemplate redis) {
        this.redis = redis;
    }

    @Override
    public Health health() {
        try {
            String pong = redis.getConnectionFactory()
                    .getConnection()
                    .ping();
            return "PONG".equalsIgnoreCase(pong)
                    ? Health.up().withDetail("ping", pong).build()
                    : Health.down().withDetail("ping", pong).build();
        } catch (Exception ex) {
            return Health.down(ex).build();
        }
    }
}

3. 健康分组:存活与就绪

K8s 场景建议拆开:

management:
  endpoint:
    health:
      probes:
        enabled: true
  health:
    livenessState:
      enabled: true
    readinessState:
      enabled: true
    group:
      readiness:
        include: readinessState,db,redis,rabbit
      liveness:
        include: livenessState
  • liveness:进程是否该被重启(尽量别绑外部依赖)
  • readiness:是否可以接流量(可绑 DB / Redis / MQ)

访问示例:

  • /actuator/health/liveness
  • /actuator/health/readiness

四、Micrometer + Prometheus 导出指标

1. 依赖

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

引入后可访问 /actuator/prometheus

2. 自动能拿到什么

在典型 Web + JDBC 应用中,常见指标包括:

  • JVM:堆内存、GC、线程数
  • HTTP:请求耗时、状态码分布
  • 连接池:活跃连接、等待线程(如 HikariCP)
  • 自定义 Timer / Counter

HikariCP 示例(确认连接池注册到 Micrometer):

spring:
  datasource:
    hikari:
      pool-name: main-pool
      maximum-pool-size: 20

3. 业务自定义指标

@Service
public class OrderMetrics {

    private final Counter paidCounter;
    private final Timer payTimer;

    public OrderMetrics(MeterRegistry registry) {
        this.paidCounter = Counter.builder("order.paid.count")
                .description("成功支付订单数")
                .register(registry);
        this.payTimer = Timer.builder("order.pay.latency")
                .description("支付耗时")
                .register(registry);
    }

    public void recordPaid(Runnable payAction) {
        payTimer.record(payAction);
        paidCounter.increment();
    }
}

4. Prometheus 抓取配置

scrape_configs:
  - job_name: spring-apps
    metrics_path: /actuator/prometheus
    static_configs:
      - targets: ['app-1:8081', 'app-2:8081']

Grafana 可直接用 JVM / Spring Boot 官方 Dashboard,再叠加业务面板(支付成功率、队列积压、连接池使用率)。


五、日志接入 TraceId(MDC)

指标告诉你「系统慢了」,日志告诉你「这一次请求慢在哪」。用 MDC(Mapped Diagnostic Context)把 traceId 写入每条日志,是成本最低的链路串联方式。

1. 过滤器生成 / 透传 TraceId

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class TraceIdFilter extends OncePerRequestFilter {

    public static final String TRACE_ID = "traceId";
    public static final String HEADER = "X-Trace-Id";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String traceId = request.getHeader(HEADER);
        if (traceId == null || traceId.isBlank()) {
            traceId = UUID.randomUUID().toString().replace("-", "");
        }
        MDC.put(TRACE_ID, traceId);
        response.setHeader(HEADER, traceId);
        try {
            filterChain.doFilter(request, response);
        } finally {
            MDC.remove(TRACE_ID);
        }
    }
}

2. Logback 模式带上 TraceId

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [traceId=%X{traceId}] - %msg%n</pattern>

日志效果:

2026-07-20 22:01:12.331 [http-nio-8080-exec-3] INFO  c.e.order.PayService [traceId=a1b2c3d4e5] - pay start
2026-07-20 22:01:12.410 [http-nio-8080-exec-3] INFO  c.e.order.PayService [traceId=a1b2c3d4e5] - pay success

排查时用 traceId 在日志平台检索,即可还原单次请求上下文。

3. 异步与线程池注意点

线程切换会丢掉 MDC。提交异步任务前要手动传递:

Map<String, String> context = MDC.getCopyOfContextMap();
executor.execute(() -> {
    if (context != null) {
        MDC.setContextMap(context);
    }
    try {
        // 业务逻辑
    } finally {
        MDC.clear();
    }
});

若已使用 Micrometer Tracing / OpenTelemetry,可进一步与 Zipkin、Jaeger 对接;MDC traceId 仍是日志侧的最小可用方案。


六、落地检查清单

接入完成后,建议按下面清单自测:

  1. /actuator/health 返回 UP,人为停 Redis 后 readiness 变为 DOWN
  2. /actuator/info 能看到构建版本或 Git 提交
  3. /actuator/prometheus 能看到 jvm_memory_used_byteshttp_server_requests_seconds
  4. 发一笔请求,日志中出现统一 traceId,响应头带回同一值
  5. Actuator 端口或路径已做鉴权 / 网络隔离

七、常见坑

现象可能原因处理
health 一直 DOWN某个弱依赖被算进默认聚合用 health group,或把非关键依赖标为非必需
Prometheus 抓不到未加 micrometer-registry-prometheus 或未暴露端点检查依赖与 exposure.include
日志没有 traceIdpattern 未写 %X{traceId},或异步丢了 MDC改 pattern,线程池透传 MDC
探活误杀实例liveness 绑了外部依赖liveness 只看进程自身

小结

  1. Actuator 解决「能不能接流量」
  2. Micrometer + Prometheus 解决「哪里在变差」
  3. MDC TraceId 解决「这一次请求怎么串起来」

三者不必一次上齐全家桶:先暴露 health,再导出 prometheus,最后补 traceId,通常一周内就能把线上可观测性从「盲飞」拉到「可定位」。后续若流量与团队规模上来,再引入完整 Tracing 与统一告警规则即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值