开发环境跑起来了,为什么生产还要调

很多团队的 Spring Boot 应用在开发环境跑得好好的,一上生产就出问题:数据库连接池打满、日志刷屏看不到错误、内存慢慢涨到 OOM、重启时正在处理的请求被中断。这些不是代码 bug,而是缺少生产级配置和监控。

Spring Boot 的自动装配在开发环境帮你做了很多合理的默认值,但生产环境的流量、并发、故障模式完全不同。你需要主动配置多环境隔离、暴露监控端点、规范日志输出、处理优雅停机——这些是让应用从"能跑"变成"能扛"的关键步骤。

开发环境的目标是快速迭代,生产环境的目标是稳定运行——两者的配置优先级完全不同。
本文核心知识点
  • 多环境application-dev/test/prod.yml 分离配置,启动时指定 profile。
  • ActuatorSpring Boot 自带的监控端点,暴露健康、指标、环境信息。
  • 日志生产日志要分级、脱敏、可检索,不要打印用户敏感数据。
  • 优雅停机收到关闭信号后先停接新请求,处理完存量请求再退出。
Spring Boot 生产监控示意:蓝色渐变背景上仪表盘和波形面板,周围铃铛图标,底部服务器机架阵列
生产环境的监控面板:仪表盘看健康、波形看趋势、铃铛管告警——应用出问题时你第一时间知道。

多环境配置:dev、test、prod 一套代码

不要把数据库密码、API 密钥硬编码在代码里。Spring Boot 的多环境配置通过 profile 机制实现:不同环境用不同的配置文件,启动时指定用哪一套。

# application.yml(公共配置)
spring:
  application:
    name: order-service
  profiles:
    active: dev  # 默认开发环境

---
# application-dev.yml
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/order_dev
    username: root
    password: dev123

---
# application-prod.yml
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://10.0.0.5:3306/order_prod
    username: order_app
    password: ${DB_PASSWORD}  # 从环境变量读取

生产环境的密码通过环境变量传入,不写在配置文件里——这样配置文件可以安全地提交到 Git。启动生产应用时指定 profile:java -jar app.jar --spring.profiles.active=prod。

Actuator:开箱即用的监控端点

Spring Boot Actuator 是生产监控的利器。引入依赖后,它自动暴露一组 HTTP 端点,告诉你应用的健康状态、内存使用、线程情况、配置信息。

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

# application-prod.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics  # 只暴露必要的端点
  endpoint:
    health:
      show-details: when-authorized  # 详情只给授权用户看

启动后访问 http://localhost:8080/actuator/health 就能看到健康状态。include 白名单很重要——不要把所有端点都暴露在公网上,env 端点会泄露所有配置信息,包括数据库密码。

健康检查与自定义指标

Actuator 自带的健康检查会检测数据库连接、磁盘空间、Redis 连接等。你也可以写自定义健康指标,比如检查外部 API 是否可用:

import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.stereotype.Component;

@Component
public class ExternalApiHealthIndicator extends AbstractHealthIndicator {

    @Override
    protected void healthCheck(Health.Builder builder) {
        try {
            // 检查外部支付 API 是否可达
            boolean ok = checkPaymentApi();
            if (ok) {
                builder.up().withDetail("payment-api", "可用");
            } else {
                builder.down().withDetail("payment-api", "不可达");
            }
        } catch (Exception e) {
            builder.down(e);
        }
    }

    private boolean checkPaymentApi() {
        // 实际发一个 HTTP 请求检查
        return true;
    }
}

K8s 的 livenessProbe 和 readinessProbe 通常就是调用 /actuator/health。健康检查返回 DOWN 时,K8s 会把这个实例从负载均衡里摘下来。