第四章:应用参数配置

说实话,应用参数配置这块,我踩过的坑比吃过的盐还多。你想想看,一个线上服务跑得好好的,突然因为某个参数没配对就挂了,那感觉真是...嗯,咱们今天就把 Spring Boot、Tomcat、Nginx、Node.js 这四个主流应用的启动参数讲透。

核心观点:启动参数不是越多越好,关键是理解每个参数背后的原理。我见过太多人把网上搜来的参数直接复制粘贴,结果出了问题都不知道怎么排查。

4.1 Spring Boot 启动参数

Spring Boot 的启动参数,说白了就是 JVM 参数 + 应用配置参数的组合。我个人习惯把它们分成三类:内存相关、调试相关、应用配置。

4.1.1 内存参数配置

先看一个我在生产环境常用的配置模板:

java -Xms2g -Xmx2g \
     -XX:MetaspaceSize=256m \
     -XX:MaxMetaspaceSize=256m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -jar app.jar

这里有几个关键点:

  • -Xms 和 -Xmx 设成一样:避免 JVM 动态调整堆大小带来的性能抖动。我曾经在一个大促项目里没注意这个,结果流量高峰时 JVM 频繁扩容,GC 时间暴涨,差点出事故。
  • MetaspaceSize 要显式指定:默认值太小,容易触发 Full GC。我记得有一次线上服务每隔半小时就 Full GC 一次,查了半天才发现是元空间扩容导致的。
  • G1GC 是标配:除非你的堆小于 4GB,否则别用 CMS 了。G1 的停顿时间可控,适合大多数场景。

小技巧:启动时加个 -XX:+PrintGCDetails,先跑几天看看 GC 日志,再调整参数。别一上来就抄网上的配置。

4.1.2 应用配置参数

Spring Boot 支持多种方式传参,我推荐用 -- 前缀的方式:

java -jar app.jar \
     --server.port=8080 \
     --spring.profiles.active=prod \
     --spring.datasource.url=jdbc:mysql://... \
     --logging.level.com.example=DEBUG

为什么要用 --?因为它会覆盖 application.yml 里的配置,而且优先级最高。我在做灰度发布时经常用这个特性,临时改个端口或者数据库连接,不用重新打包。

注意:敏感信息(密码、密钥)别直接写在命令行里。用环境变量或者配置中心,比如 SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD}。我曾经见过有人把数据库密码直接写在启动脚本里,结果代码库泄露后整个库都被拖走了。

4.2 Tomcat 启动参数

Tomcat 的启动参数主要分两块:JVM 参数(通过 CATALINA_OPTS)和 Tomcat 自身配置(通过 server.xml)。

4.2.1 JVM 参数配置

在 catalina.sh 或 setenv.sh 里设置:

export CATALINA_OPTS="-Xms2g -Xmx2g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -Djava.security.egd=file:/dev/./urandom"

最后那个 -Djava.security.egd 参数,很多人会忽略。Tomcat 启动时如果用到 SecureRandom,默认会从 /dev/random 读取,这个设备在熵池不足时会阻塞。我曾经在测试环境等 Tomcat 启动等了 5 分钟,查了半天才发现是这个原因。改成 /dev/./urandom 后,启动时间从 5 分钟降到了 10 秒。

4.2.2 连接器参数

在 server.xml 里配置:

<Connector port="8080" 
           protocol="HTTP/1.1"
           maxThreads="200"
           minSpareThreads="25"
           acceptCount="100"
           connectionTimeout="20000"
           maxConnections="10000" />

这些参数怎么配?我一般遵循这个原则:

  • maxThreads:不是越大越好。我见过有人设成 1000,结果 CPU 上下文切换开销直接占满。一般 200-500 就够用了。
  • acceptCount:排队队列长度。如果请求量突然暴增,这个值能起到缓冲作用。但别设太大,否则客户端会超时重试,反而加重服务器负担。
  • maxConnections:Tomcat 能同时处理的最大连接数。超过这个值的连接会被拒绝。我建议设成 maxThreads 的 2-3 倍。

经验之谈:我曾经接手过一个项目,Tomcat 的 maxThreads 设成了 50,但业务高峰期并发请求有 200 多。结果 150 个请求在排队,平均响应时间从 200ms 飙升到 5 秒。调大 maxThreads 后,问题立刻解决了。

4.3 Nginx 启动参数

Nginx 的启动参数主要在 nginx.conf 里。我把它分成核心参数和性能参数两类。

4.3.1 核心参数

worker_processes  auto;
worker_rlimit_nofile 65535;

events {
    worker_connections  10240;
    use epoll;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;
}

这里有几个关键点:

  • worker_processes auto:自动匹配 CPU 核心数。我建议别手动指定,除非你的机器有特殊的 NUMA 架构。
  • worker_connections:每个 worker 能处理的最大连接数。总连接数 = worker_processes × worker_connections。我一般按 10240 起步,根据内存和业务量调整。
  • use epoll:Linux 下最高效的事件驱动模型。别用 select 或 poll,性能差一个数量级。

避坑指南:我曾经把 worker_rlimit_nofile 设得太小,结果 Nginx 报 "too many open files" 错误。这个值要大于 worker_connections × 2,因为每个连接至少占用两个文件描述符。

4.3.2 性能调优参数

http {
    client_body_buffer_size 128k;
    client_max_body_size 10m;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    proxy_busy_buffers_size 8k;
    
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 2;
    gzip_types text/plain application/javascript text/css;
}

gzip 压缩这块,我建议压缩级别别超过 2。级别高了 CPU 开销大,但压缩率提升有限。我曾经测试过,级别 2 和级别 9 的压缩率只差 5%,但 CPU 使用率差了 3 倍。

4.4 Node.js 启动参数

Node.js 的启动参数主要通过 NODE_OPTIONS 环境变量或者命令行参数传入。

4.4.1 内存参数

NODE_OPTIONS="--max-old-space-size=2048 --max-semi-space-size=512" node app.js

Node.js 的 V8 引擎内存管理跟 JVM 不太一样:

  • --max-old-space-size:老生代内存上限。我一般设成 2GB-4GB,具体看业务。超过这个值会触发垃圾回收,如果回收后还超,进程就挂了。
  • --max-semi-space-size:新生代内存。这个值影响 GC 频率。设小了 GC 频繁,设大了单次 GC 时间长。我一般用 512MB 起步。

注意:Node.js 默认内存限制是 512MB(32位)或 1GB(64位)。如果你不做任何配置,内存密集型应用很容易 OOM。我曾经有个数据处理服务,处理 100MB 的 JSON 文件就挂了,加上 --max-old-space-size=4096 后稳如老狗。

4.4.2 调试与性能参数

NODE_OPTIONS="--inspect=0.0.0.0:9229 --trace-warnings --pending-deprecation" node app.js

这几个参数我经常用:

  • --inspect:开启调试端口。生产环境别开,除非你确定防火墙规则没问题。
  • --trace-warnings:打印警告的堆栈信息。排查问题时特别有用。
  • --pending-deprecation:显示即将废弃的 API 警告。我建议在测试环境开启,提前发现兼容性问题。

4.4.3 集群模式参数

node --experimental-cluster-reload cluster.js

Node.js 是单线程的,要充分利用多核 CPU,得用 cluster 模块。我一般这样启动:

const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  // 工作进程代码
}

这里要注意,每个 worker 进程都会占用一份内存。如果你的应用内存占用大,worker 数量别超过 CPU 核心数,否则内存会爆。

总结一下:应用参数配置没有银弹。每个参数都要结合你的业务场景、硬件配置、流量模型来调整。我的习惯是:先用默认参数跑起来,然后通过监控数据逐步调优。别一上来就搞一堆参数,出了问题都不知道是哪个引起的。

应用参数配置知识体系 Spring Boot Tomcat Nginx Node.js JVM内存参数 应用配置参数 GC调优参数 CATALINA_OPTS 连接器参数 线程池配置 核心参数 性能调优参数 Gzip压缩配置 内存参数 调试参数 集群模式 核心原则:理解原理 → 监控验证 → 逐步调优 没有银弹,每个参数都要结合业务场景调整

可可精品资料库