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&amp;characterEncoding=utf8&amp;useSSL=false&amp;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

小结

通过本文学习,你应该掌握:

  1. Tomcat性能优化的整体策略和层次
  2. JVM参数调优和垃圾回收器配置
  3. 连接器和线程池的性能优化
  4. 应用配置和静态资源优化
  5. 数据库连接池的性能配置
  6. 系统级的优化设置
  7. 性能监控和压力测试方法
  8. JVM性能分析和问题诊断

这些优化技巧能够显著提升Tomcat应用的性能表现。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""