Tomcat 性能优化
Performance Optimization
概述
Tomcat性能优化是确保Web应用高效运行的关键。本文将从JVM调优、连接器优化、应用配置等多个维度介绍Tomcat性能优化的方法和技巧。
1. 性能优化概览
1.1 性能优化层次
性能优化层次
├── 系统级优化
│ ├── 操作系统配置
│ ├── 硬件资源
│ └── 网络配置
├── JVM级优化
│ ├── 堆内存配置
│ ├── 垃圾回收器选择
│ └── JVM参数调优
├── Tomcat级优化
│ ├── 连接器配置
│ ├── 线程池调优
│ └── 缓存配置
└── 应用级优化
├── 代码优化
├── 数据库优化
└── 静态资源优化
1.2 性能指标
# 关键性能指标
- 吞吐量 (Throughput): 每秒处理的请求数
- 响应时间 (Response Time): 请求处理时间
- 并发用户数 (Concurrent Users): 同时在线用户数
- CPU利用率 (CPU Usage): 处理器使用率
- 内存使用率 (Memory Usage): 内存占用情况
- 错误率 (Error Rate): 请求失败比例
2. JVM性能调优
2.1 堆内存配置
# setenv.sh - 基础内存配置
export CATALINA_OPTS="$CATALINA_OPTS -Xms2048m" # 初始堆大小
export CATALINA_OPTS="$CATALINA_OPTS -Xmx4096m" # 最大堆大小
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=256m" # 元空间初始大小
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=512m" # 元空间最大大小
# 新生代配置
export CATALINA_OPTS="$CATALINA_OPTS -XX:NewRatio=3" # 新生代与老年代比例
export CATALINA_OPTS="$CATALINA_OPTS -XX:SurvivorRatio=8" # Eden与Survivor比例
# 直接内存配置
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxDirectMemorySize=1024m"
2.2 垃圾回收器配置
# G1GC配置(推荐用于大堆内存)
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1HeapRegionSize=16m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1NewSizePercent=30"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1MaxNewSizePercent=40"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1MixedGCCountTarget=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=45"
# ParallelGC配置(适用于吞吐量优先场景)
# export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseParallelGC"
# export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
# export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=150"
# ZGC配置(Java 11+,超低延迟)
# export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockExperimentalVMOptions"
# export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseZGC"
2.3 GC日志配置
# GC日志配置(Java 8)
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCTimeStamps"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDateStamps"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseGCLogFileRotation"
export CATALINA_OPTS="$CATALINA_OPTS -XX:NumberOfGCLogFiles=5"
export CATALINA_OPTS="$CATALINA_OPTS -XX:GCLogFileSize=100M"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:$CATALINA_HOME/logs/gc.log"
# GC日志配置(Java 11+)
export CATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:$CATALINA_HOME/logs/gc.log:time,pid,tid"
export CATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:gc.log:time:filecount=5,filesize=100M"
2.4 其他JVM优化参数
# 性能优化参数
export CATALINA_OPTS="$CATALINA_OPTS -server" # 服务器模式
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseFastAccessorMethods" # 快速访问器
export CATALINA_OPTS="$CATALINA_OPTS -XX:+AggressiveOpts" # 激进优化
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseBiasedLocking" # 偏向锁
export CATALINA_OPTS="$CATALINA_OPTS -XX:+DisableExplicitGC" # 禁用显式GC
# 字符串优化
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseStringDeduplication" # 字符串去重
export CATALINA_OPTS="$CATALINA_OPTS -XX:+OptimizeStringConcat" # 字符串连接优化
# 编译优化
export CATALINA_OPTS="$CATALINA_OPTS -XX:CompileThreshold=1500" # 编译阈值
export CATALINA_OPTS="$CATALINA_OPTS -XX:+TieredCompilation" # 分层编译
# 内存优化
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseCompressedOops" # 压缩指针
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseCompressedClassPointers" # 压缩类指针
3. 连接器性能优化
3.1 线程池优化
<!-- 高性能线程池配置 -->
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="400" <!-- 根据CPU核心数调整 -->
minSpareThreads="50" <!-- 预热线程 -->
maxIdleTime="60000" <!-- 空闲超时 -->
maxQueueSize="100" <!-- 队列大小 -->
prestartminSpareThreads="true" <!-- 预启动线程 -->
threadPriority="5" <!-- 线程优先级 -->
daemon="false"/> <!-- 非守护线程 -->
<Connector executor="tomcatThreadPool"
port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="10000" <!-- 减少连接超时 -->
keepAliveTimeout="15000" <!-- Keep-Alive超时 -->
maxKeepAliveRequests="200" <!-- 增加Keep-Alive请求数 -->
acceptCount="200" <!-- 接受队列 -->
enableLookups="false" <!-- 禁用DNS查找 -->
tcpNoDelay="true" <!-- 禁用Nagle算法 -->
compression="on" <!-- 启用压缩 -->
compressionMinSize="2048"/> <!-- 压缩阈值 -->
3.2 NIO2连接器优化
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
maxThreads="300"
acceptCount="150"
connectionTimeout="8000"
asyncTimeout="5000"
socket.directBuffer="true" <!-- 使用直接缓冲区 -->
socket.directSslBuffer="false" <!-- SSL使用堆缓冲区 -->
socket.appReadBufSize="8192" <!-- 应用读缓冲区 -->
socket.appWriteBufSize="8192" <!-- 应用写缓冲区 -->
socket.bufferPool="500" <!-- 缓冲池大小 -->
socket.bufferPoolSize="1048576"/> <!-- 缓冲池总大小 -->
3.3 APR连接器优化
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="400"
acceptCount="200"
connectionTimeout="8000"
keepAliveTimeout="15000"
pollTime="2000" <!-- 轮询时间 -->
pollerSize="32768" <!-- 轮询器大小 -->
sendfileSize="102400" <!-- Sendfile大小 -->
deferAccept="true" <!-- 延迟接受 -->
useSendfile="true"/> <!-- 使用Sendfile -->
4. 应用配置优化
4.1 Context性能配置
<Context reloadable="false" <!-- 生产环境禁用重载 -->
backgroundProcessorDelay="30" <!-- 后台处理延迟 -->
scanClassPath="false" <!-- 禁用类路径扫描 -->
scanManifest="false" <!-- 禁用清单扫描 -->
antiResourceLocking="false" <!-- 禁用资源锁定保护 -->
antiJARLocking="false"> <!-- 禁用JAR锁定保护 -->
<!-- 资源缓存 -->
<Resources cachingAllowed="true"
cacheMaxSize="102400" <!-- 100MB缓存 -->
cacheTtl="300000" <!-- 5分钟TTL -->
cacheObjectMaxSize="4096"/> <!-- 4KB最大对象 -->
<!-- 会话配置 -->
<Manager className="org.apache.catalina.session.StandardManager"
maxActiveSessions="1000" <!-- 最大活跃会话 -->
sessionIdLength="32" <!-- 会话ID长度 -->
randomClass="java.security.SecureRandom"/>
</Context>
4.2 静态资源优化
<!-- 静态资源Servlet优化 -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value> <!-- 禁用目录列表 -->
</init-param>
<init-param>
<param-name>gzip</param-name>
<param-value>true</param-value> <!-- 启用Gzip -->
</init-param>
<init-param>
<param-name>sendfileSize</param-name>
<param-value>102400</param-value> <!-- Sendfile阈值 -->
</init-param>
<init-param>
<param-name>useAcceptRanges</param-name>
<param-value>true</param-value> <!-- 支持断点续传 -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
4.3 JSP性能优化
<!-- JSP Servlet优化 -->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value> <!-- 不fork进程 -->
</init-param>
<init-param>
<param-name>development</param-name>
<param-value>false</param-value> <!-- 生产模式 -->
</init-param>
<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>11</param-value> <!-- Java版本 -->
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>11</param-value> <!-- 目标Java版本 -->
</init-param>
<init-param>
<param-name>genStrAsCharArray</param-name>
<param-value>true</param-value> <!-- 字符数组优化 -->
</init-param>
<init-param>
<param-name>trimSpaces</param-name>
<param-value>true</param-value> <!-- 去除空白 -->
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
5. 数据库连接池优化
5.1 Tomcat JDBC Pool配置
<Resource name="jdbc/OptimizedDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"
username="dbuser"
password="dbpass"
<!-- 连接池大小 -->
initialSize="10" <!-- 初始连接数 -->
maxActive="100" <!-- 最大活跃连接 -->
maxIdle="30" <!-- 最大空闲连接 -->
minIdle="10" <!-- 最小空闲连接 -->
<!-- 超时配置 -->
maxWait="10000" <!-- 最大等待时间 -->
maxAge="600000" <!-- 连接最大年龄 -->
<!-- 验证配置 -->
testOnBorrow="true" <!-- 借用时测试 -->
testOnReturn="false" <!-- 返还时测试 -->
testWhileIdle="true" <!-- 空闲时测试 -->
validationQuery="SELECT 1" <!-- 验证查询 -->
validationInterval="30000" <!-- 验证间隔 -->
<!-- 回收配置 -->
timeBetweenEvictionRunsMillis="30000" <!-- 回收线程运行间隔 -->
minEvictableIdleTimeMillis="60000" <!-- 最小可回收空闲时间 -->
removeAbandoned="true" <!-- 移除废弃连接 -->
removeAbandonedTimeout="300" <!-- 废弃超时时间 -->
logAbandoned="true" <!-- 记录废弃连接 -->
<!-- 性能配置 -->
useEquals="false" <!-- 使用==比较 -->
useLock="false" <!-- 禁用锁 -->
fairQueue="false" <!-- 非公平队列 -->
jmxEnabled="true" <!-- 启用JMX -->
<!-- 连接属性 -->
connectionProperties="useServerPrepStmts=true;cachePrepStmts=true;prepStmtCacheSize=250;prepStmtCacheSqlLimit=2048;useLocalSessionState=true;useLocalTransactionState=true"/>
5.2 HikariCP配置
<Resource name="jdbc/HikariDB"
auth="Container"
type="javax.sql.DataSource"
factory="com.zaxxer.hikari.HikariJNDIFactory"
driverClassName="com.mysql.cj.jdbc.Driver"
jdbcUrl="jdbc:mysql://localhost:3306/mydb"
username="dbuser"
password="dbpass"
<!-- 连接池配置 -->
minimumIdle="10" <!-- 最小空闲连接 -->
maximumPoolSize="50" <!-- 最大连接池大小 -->
connectionTimeout="20000" <!-- 连接超时 -->
idleTimeout="300000" <!-- 空闲超时 -->
maxLifetime="1200000" <!-- 连接最大生命周期 -->
<!-- 性能配置 -->
prepStmtCacheSize="250" <!-- 预编译语句缓存 -->
prepStmtCacheSqlLimit="2048" <!-- 预编译语句大小限制 -->
cachePrepStmts="true" <!-- 缓存预编译语句 -->
useServerPrepStmts="true"/> <!-- 使用服务器预编译语句 -->
6. 系统级优化
6.1 Linux系统优化
#!/bin/bash
# system-optimization.sh
# 修改系统限制
echo "* soft nofile 65535" >> /etc/security/limits.conf
echo "* hard nofile 65535" >> /etc/security/limits.conf
echo "* soft nproc 32768" >> /etc/security/limits.conf
echo "* hard nproc 32768" >> /etc/security/limits.conf
# 内核参数优化
cat >> /etc/sysctl.conf << EOF
# 网络优化
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 6000
# TCP优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15
# 内存优化
vm.swappiness = 1
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.max_map_count = 262144
# 文件系统优化
fs.file-max = 6815744
EOF
# 应用配置
sysctl -p
6.2 文件系统优化
# 挂载选项优化
mount -o remount,noatime,nodiratime /
echo "/dev/sda1 / ext4 defaults,noatime,nodiratime 0 1" >> /etc/fstab
# 磁盘调度器优化
echo noop > /sys/block/sda/queue/scheduler
# 文件描述符优化
echo 'ulimit -n 65535' >> /etc/profile
7. 性能监控和测试
7.1 性能监控脚本
#!/bin/bash
# performance-monitor.sh
CATALINA_HOME="/opt/tomcat9"
LOG_FILE="/var/log/tomcat-performance.log"
monitor_performance() {
echo "=== $(date) ===" >> $LOG_FILE
# JVM内存使用
JAVA_PID=$(pgrep -f "org.apache.catalina.startup.Bootstrap")
if [ -n "$JAVA_PID" ]; then
echo "JVM内存使用:" >> $LOG_FILE
jstat -gc $JAVA_PID | tail -1 >> $LOG_FILE
# GC统计
echo "GC统计:" >> $LOG_FILE
jstat -gccapacity $JAVA_PID | tail -1 >> $LOG_FILE
# 线程信息
echo "线程数: $(jstack $JAVA_PID | grep "^\"" | wc -l)" >> $LOG_FILE
fi
# 系统资源
echo "CPU使用率: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)" >> $LOG_FILE
echo "内存使用: $(free | grep Mem | awk '{printf "%.2f%%", $3/$2 * 100.0}')" >> $LOG_FILE
# 网络连接
echo "TCP连接数: $(netstat -an | grep ESTABLISHED | wc -l)" >> $LOG_FILE
echo "TIME_WAIT连接数: $(netstat -an | grep TIME_WAIT | wc -l)" >> $LOG_FILE
echo "" >> $LOG_FILE
}
# 每5分钟监控一次
while true; do
monitor_performance
sleep 300
done
7.2 性能压力测试
#!/bin/bash
# performance-test.sh
# 使用Apache Bench进行测试
test_with_ab() {
local url=$1
local requests=${2:-1000}
local concurrency=${3:-100}
echo "=== Apache Bench测试 ==="
echo "URL: $url"
echo "请求数: $requests"
echo "并发数: $concurrency"
ab -n $requests -c $concurrency -g test_results.dat $url
}
# 使用wrk进行测试
test_with_wrk() {
local url=$1
local duration=${2:-30s}
local threads=${3:-4}
local connections=${4:-100}
echo "=== wrk测试 ==="
echo "URL: $url"
echo "持续时间: $duration"
echo "线程数: $threads"
echo "连接数: $connections"
wrk -t$threads -c$connections -d$duration --latency $url
}
# 综合测试
comprehensive_test() {
local base_url="http://localhost:8080"
echo "开始性能测试..."
# 静态资源测试
test_with_ab "$base_url/static/test.html" 5000 200
# 动态内容测试
test_with_ab "$base_url/dynamic/test.jsp" 2000 100
# API测试
test_with_wrk "$base_url/api/test" 60s 8 200
echo "测试完成"
}
case "$1" in
"ab") test_with_ab "$2" "$3" "$4" ;;
"wrk") test_with_wrk "$2" "$3" "$4" "$5" ;;
"full") comprehensive_test ;;
*) echo "用法: $0 {ab|wrk|full} [参数...]" ;;
esac
7.3 JVM性能分析
#!/bin/bash
# jvm-analysis.sh
JAVA_PID=$(pgrep -f "org.apache.catalina.startup.Bootstrap")
if [ -z "$JAVA_PID" ]; then
echo "Tomcat进程未找到"
exit 1
fi
echo "=== JVM性能分析 ==="
echo "进程ID: $JAVA_PID"
# 堆内存使用情况
echo "1. 堆内存使用:"
jmap -heap $JAVA_PID
# GC统计
echo "2. GC统计:"
jstat -gc $JAVA_PID
# 类加载统计
echo "3. 类加载统计:"
jstat -class $JAVA_PID
# 编译统计
echo "4. 编译统计:"
jstat -compiler $JAVA_PID
# 线程dump
echo "5. 生成线程dump..."
jstack $JAVA_PID > thread_dump_$(date +%Y%m%d_%H%M%S).txt
# 堆dump(当内存使用率过高时)
HEAP_USAGE=$(jstat -gc $JAVA_PID | tail -1 | awk '{print ($3+$4+$6+$8)/($1+$2+$5+$7)*100}')
if (( $(echo "$HEAP_USAGE > 80" | bc -l) )); then
echo "6. 内存使用率过高($HEAP_USAGE%),生成堆dump..."
jmap -dump:format=b,file=heap_dump_$(date +%Y%m%d_%H%M%S).hprof $JAVA_PID
fi
小结
通过本文学习,你应该掌握:
- Tomcat性能优化的整体策略和层次
- JVM参数调优和垃圾回收器配置
- 连接器和线程池的性能优化
- 应用配置和静态资源优化
- 数据库连接池的性能配置
- 系统级的优化设置
- 性能监控和压力测试方法
- JVM性能分析和问题诊断
这些优化技巧能够显著提升Tomcat应用的性能表现。