上个月帮一个团队做上线前的Code Review,翻了12个Spring Boot项目,有9个项目的核心配置项是默认的。开发用默认配置在本地跑得好好的,扔到生产就炸——Tomcat线程数200不够、HikariCP连接池10个不够、Actuator全暴露被扫描、Druid慢SQL没人看。
任何一个配置项,单独看都不致命。但高并发场景下,这些配置项中的任何一个缺失,都会直接导致502。
这不是配置"调优"的问题,是配置"有没有"的问题。下面5项是Spring Boot上线前的最低配置项清单,每一项都有血淋淋的案例。
1
Tomcat线程数:默认200?大促直接排队到超时
事故场景:某电商大促峰值QPS 3000,服务从200线程的Tomcat里拿出400个线程处理请求——结果有300+个请求在队列里等着,排队超过2秒直接超时。监控显示:Tomcat线程池使用率100%,accept-count队列从50个满到500个。
默认配置的问题:
# Spring Boot 2.x 默认值
server.tomcat.threads.max=200 # 最大工作线程
server.tomcat.max-connections=8192 # 最大连接数
server.tomcat.accept-count=100 # 等待队列长度
server.tomcat.connection-timeout=5s # 连接超时
# 大促场景:200线程处理3000 QPS,每个请求平均100ms
# → 200 / 0.1 = 2000 QPS上限
# → 峰值3000 QPS直接超出40%
正确配置(按业务估算):
# 业务侧需求:峰值QPS 3000,平均响应时间 100ms
# 最低线程数 = 3000 × 0.1 = 300个
# 建议值:按峰值QPS × 平均RT的1.5倍配置,留出30%buffer
server.tomcat.threads.max=500 # 峰值时段留buffer
server.tomcat.min-spare-threads=50 # 最小空闲线程,避免冷启动排队
server.tomcat.max-connections=10000 # 略大于Tomcat线程数
server.tomcat.accept-count=200 # 队列不能太长,太长等于502
server.tomcat.connection-timeout=10s
经验值:Tomcat线程数 = 峰值QPS × 平均RT × 1.5。3000 QPS × 0.1s × 1.5 = 450个,至少配到500。不要拍脑袋配200,那是开发机的值。
2
HikariCP连接池:默认10?QPS超过50就排队
事故场景:某金融系统上线后白天正常,一到晚上批量任务时段QPS从30涨到80,HikariCP连接池使用率持续100%,慢SQL数量从1条/小时涨到200+条/小时。监控显示:getConnection()等待时间从2ms涨到800ms。
默认配置的问题:
# Spring Boot 2.x 默认值(HikariCP)
spring.datasource.hikari.maximum-pool-size=10 # 池大小
spring.datasource.hikari.minimum-idle=10 # 最小空闲
spring.datasource.hikari.connection-timeout=30000 # 30秒
spring.datasource.hikari.idle-timeout=600000 # 10分钟
spring.datasource.hikari.max-lifetime=1800000 # 30分钟
# 问题:10个连接,假设每个查询100ms
# → 10 / 0.1 = 100 QPS上限
# → 超过100 QPS的请求开始等待连接 → 慢SQL
正确配置(按数据库能力估算):
# 先查数据库最大连接数:show variables like 'max_connections';
# 一般MySQL默认151个,全部分给业务大概有120个可用
# 单实例应用:池大小 ≤ 30(留buffer给其他应用)
# 多实例应用:池大小 = 总可用连接数 / 实例数 - 5
spring.datasource.hikari.maximum-pool-size=30 # 单实例不超过30
spring.datasource.hikari.minimum-idle=10 # 保留10个空闲
spring.datasource.hikari.connection-timeout=3000 # 3秒拿不到连接直接报错
spring.datasource.hikari.idle-timeout=300000 # 5分钟回收空闲
spring.datasource.hikari.max-lifetime=1200000 # 20分钟强制重建,避免数据库单方面断开
spring.datasource.hikari.leak-detection-threshold=30000 # 30秒未释放视为泄漏
HikariCP官方建议池大小 = ((核心数 × 2) + 1) 个硬盘连接数,但实际按数据库承载能力配更合理。MySQL默认151连接,20个实例 × 30连接 = 600?不行,MySQL扛不住。单个应用连接池 ≤ 数据库max_connections的30%。
3
Actuator端点:全暴露?等于把数据库连接池监控送人
事故场景:某团队Actuator端点暴露在公网,/actuator/env直接返回数据库密码、Redis密码、JWT密钥。安全扫描工具10秒扫到,30分钟内被脱裤。事后复盘:开发说"我以为默认就是不暴露的",实际上Spring Boot 2.x之后默认就暴露/actuator/health和/actuator/info,而/actuator/*全暴露是开发本地配置的。
默认配置的问题:
# Spring Boot 2.x 之后,Actuator只暴露health和info
# 但很多人为了"方便"会写:
management.endpoints.web.exposure.include=* # ⚠️ 全暴露!
# 等于把下面这些全部开放到公网:
# /actuator/env → 环境变量(含数据库密码)
# /actuator/configprops → 完整配置(含明文密码)
# /actuator/beans → 所有Bean
# /actuator/mappings → 所有URL映射
# /actuator/threaddump → 线程栈(含业务逻辑)
# /actuator/heapdump → 完整内存镜像
正确配置(最小暴露原则):
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus # 只暴露监控需要的
exclude: env,configprops,beans,mappings,threaddump,heapdump
endpoint:
health:
show-details: never # 不显示详细信息,只返回UP/DOWN
server:
address: 127.0.0.1 # 关键!Actuator只监听本地,通过Prometheus从本地拉
# 或者放到内网端口,不走主端口
management.server.port: 9090
management.server.address: 10.0.0.0/8 # 只允许内网访问
Actuator的heapdump端点是最危险的——它返回完整JVM堆内存镜像,里面有所有缓存的密码、用户token。绝对不能暴露在公网,必须限制访问来源IP。
4
日志级别:默认INFO?生产环境全打就完了
事故场景:某团队从DEBUG切回INFO时漏改了一个第三方库的level,MyBatis的SQL日志每天打500MB,一周后磁盘写满服务挂了。更极端的案例:把整个Spring Framework的日志级别配成DEBUG,单服务每天日志量8GB。
默认配置的问题:
# Spring Boot默认root level = INFO
# 但很多团队没显式配,导致:
# 1. 业务代码的DEBUG也漏出来
# 2. 第三方库(Spring/MyBatis/HikariCP)全部INFO
# 3. 大促期间日志量翻3-5倍
# MyBatis默认DEBUG,把每条SQL+参数都打出来
# HikariCP默认DEBUG,把连接池状态都打出来
# Spring Web默认INFO,把每次请求都打出来
正确配置(生产环境分级):
# application-prod.yml
logging:
level:
root: INFO # 默认INFO
com.example.business: INFO # 业务代码
org.springframework: WARN # Spring框架
org.mybatis: WARN # MyBatis关掉
com.zaxxer.hikari: WARN # HikariCP关掉
org.apache.tomcat: WARN # Tomcat关掉
file:
name: /var/log/app/app.log
max-size: 100MB # 单文件100MB
max-history: 7 # 保留7天
total-size-cap: 10GB # 总大小不超过10GB
生产环境:业务代码INFO、第三方库全部WARN或OFF。业务级INFO + 第三方WARN,日志量能从500MB/天降到50MB/天。
5
Profile激活:忘了指定?配置文件全乱套
事故场景:某团队启动命令没指定profile,Spring Boot默认加载application.yml,直接连到了测试库的数据库。测试数据被生产服务读出来,用户看到一堆测试账号,运营群里炸了。修复花了2小时,事故报告写了8页。
默认配置的问题:
# 没有指定profile时,Spring Boot只加载 application.yml
# 而 application.yml 里通常写的是开发配置:
# application.yml(开发配置)
spring:
datasource:
url: jdbc:mysql://192.168.1.100:3306/test_db
username: dev_user
password: dev_pass
logging:
level:
com.example.business: DEBUG
正确配置(强制Profile):
# application.yml(公共配置)
spring:
profiles:
active: dev # 开发默认dev
application:
name: my-service
# application-prod.yml(生产配置)
spring:
datasource:
url: jdbc:mysql://prod-db:3306/prod_db
username: ${DB_USER} # 从环境变量读
password: ${DB_PASS}
logging:
level:
com.example.business: INFO
org.springframework: WARN
生产环境启动时强制指定profile:
# K8s deployment
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod" # 强制prod,dev/staging都不会生效
# 启动命令
java -jar -Dspring.profiles.active=prod app.jar
# 或者用环境变量
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar
经验:生产环境的启动命令必须显式指定profile,并且profile配置必须和默认配置严格区分。配置文件结构建议:application.yml(公共)+ application-{profile}.yml(环境特定),profile从环境变量读,不允许在代码里硬编码。
5项配置速查表
这5项是Spring Boot上线前的最低必查项,不是"建议配置"。任何一项缺失,在生产环境高并发场景下都会直接暴露问题。配置项之间不冲突,可以一次性全部加上。
配置检查清单(打印出来贴工位)
□ Tomcat线程数按QPS估算(不小于500)
□ HikariCP连接池按DB能力估算(不小于20)
□ Actuator端点最小暴露(不包含env/heapdump)
□ 生产日志级别(业务INFO + 第三方WARN)
□ 启动命令显式指定prod profile
□ 配置文件分环境(application-{profile}.yml)
□ 敏感配置从环境变量读(不用明文密码)
□ 配置变更记录到Git(不直接在服务器改)
Spring Boot配置检查,飞算JavaAI帮你30秒扫完
这5项配置在Spring Boot项目里非常容易遗漏——特别是HikariCP连接池大小和Actuator端点暴露,靠人工Code Review经常漏掉。飞算JavaAI的代码审查器可以自动扫描项目中的application.yml、application.properties、logback.xml等配置文件,检查生产环境关键配置项是否合理,包括Tomcat线程数是否够用、HikariCP连接池是否合理、Actuator端点是否过度暴露、生产环境日志级别是否过高等。30秒跑完5项检查,给出具体的修复建议和示例配置。
你的Spring Boot项目里,这5项配置有几项是默认值的?