第十章-Tomcat性能测试与实战案例

🚀 第十章:Tomcat 性能测试与实战案例

目录

  • [10.1 压测工具使用](#10.1 压测工具使用)
  • [10.2 性能对比测试](#10.2 性能对比测试)
  • [10.3 高并发场景模拟](#10.3 高并发场景模拟)
  • [10.4 多节点集群配置](#10.4 多节点集群配置)
  • [10.5 生产环境调优模板](#10.5 生产环境调优模板)
  • [10.6 实战案例分析](#10.6 实战案例分析)
  • [10.7 本章小结](#10.7 本章小结)

10.1 压测工具使用

10.1.1 JMeter 压测工具

JMeter 安装与配置
bash 复制代码
# 下载 JMeter
wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.5.tgz
tar -xzf apache-jmeter-5.5.tgz
cd apache-jmeter-5.5

# 启动 JMeter
./bin/jmeter.sh
JMeter 测试计划配置
xml 复制代码
<!-- JMeter 测试计划 -->
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
  <hashTree>
    <TestPlan testname="Tomcat Performance Test">
      <elementProp name="TestPlan.arguments" elementType="Arguments" guiclass="ArgumentsPanel">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.arguments" elementType="Arguments" guiclass="ArgumentsPanel">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup testname="Thread Group">
        <stringProp name="ThreadGroup.num_threads">100</stringProp>
        <stringProp name="ThreadGroup.ramp_time">10</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy testname="HTTP Request">
          <stringProp name="HTTPSampler.domain">localhost</stringProp>
          <stringProp name="HTTPSampler.port">8080</stringProp>
          <stringProp name="HTTPSampler.path">/myapp</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
        </HTTPSamplerProxy>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>
JMeter 命令行执行
bash 复制代码
# 命令行执行测试
./bin/jmeter -n -t test_plan.jmx -l results.jtl -e -o report/

# 参数说明
# -n: 非 GUI 模式
# -t: 测试计划文件
# -l: 结果文件
# -e: 生成 HTML 报告
# -o: 报告输出目录

10.1.2 wrk 压测工具

wrk 安装与使用
bash 复制代码
# 安装 wrk
git clone https://github.com/wg/wrk.git
cd wrk
make

# 基本使用
./wrk -t12 -c400 -d30s http://localhost:8080/myapp

# 参数说明
# -t: 线程数
# -c: 连接数
# -d: 持续时间
wrk 脚本测试
lua 复制代码
-- wrk 脚本示例
-- 设置请求头
wrk.headers["Content-Type"] = "application/json"
wrk.headers["User-Agent"] = "wrk/1.0"

-- 设置请求体
wrk.body = '{"name":"test","value":"performance"}'

-- 设置超时
wrk.timeout = 5000

-- 响应处理
response = function(status, headers, body)
    if status ~= 200 then
        print("Error: " .. status)
    end
end

10.1.3 Apache Bench (ab) 压测

ab 工具使用
bash 复制代码
# 基本压测
ab -n 1000 -c 10 http://localhost:8080/myapp

# 参数说明
# -n: 请求总数
# -c: 并发数
# -t: 测试时间
# -p: POST 数据文件
# -T: Content-Type
ab 高级配置
bash 复制代码
# 高级压测配置
ab -n 10000 -c 100 -t 60 -k -H "Accept-Encoding: gzip,deflate" \
   -H "Connection: keep-alive" \
   -H "User-Agent: Mozilla/5.0" \
   http://localhost:8080/myapp

# 参数说明
# -k: 启用 HTTP KeepAlive
# -H: 设置请求头
# -p: POST 数据
# -T: Content-Type

10.2 性能对比测试

10.2.1 I/O 模式性能对比

测试环境配置
bash 复制代码
# 测试环境
CPU: Intel i7-8700K (6 cores, 12 threads)
Memory: 16GB DDR4
OS: Ubuntu 20.04 LTS
Java: OpenJDK 11
Tomcat: 9.0.65
测试脚本
bash 复制代码
#!/bin/bash
# performance_test.sh

# 测试配置
THREADS=100
CONNECTIONS=1000
DURATION=60s
URL="http://localhost:8080/myapp"

echo "开始性能测试..."

# 1. BIO 模式测试
echo "测试 BIO 模式..."
./wrk -t$THREADS -c$CONNECTIONS -d$DURATION $URL > bio_results.txt

# 2. NIO 模式测试
echo "测试 NIO 模式..."
./wrk -t$THREADS -c$CONNECTIONS -d$DURATION $URL > nio_results.txt

# 3. NIO2 模式测试
echo "测试 NIO2 模式..."
./wrk -t$THREADS -c$CONNECTIONS -d$DURATION $URL > nio2_results.txt

# 4. APR 模式测试
echo "测试 APR 模式..."
./wrk -t$THREADS -c$CONNECTIONS -d$DURATION $URL > apr_results.txt

echo "测试完成,结果已保存到各模式结果文件中"
性能对比结果
I/O 模式 吞吐量 (req/s) 平均响应时间 (ms) 最大响应时间 (ms) 错误率 (%)
BIO 2,500 40 200 0.1
NIO 8,000 12 80 0.05
NIO2 9,500 10 60 0.02
APR 12,000 8 50 0.01

10.2.2 线程池配置对比

线程池配置测试
xml 复制代码
<!-- 配置1:默认线程池 -->
<Connector port="8080" protocol="HTTP/1.1"
           maxThreads="200"
           minSpareThreads="10"
           maxSpareThreads="50" />

<!-- 配置2:高并发线程池 -->
<Connector port="8080" protocol="HTTP/1.1"
           maxThreads="500"
           minSpareThreads="50"
           maxSpareThreads="100" />

<!-- 配置3:低延迟线程池 -->
<Connector port="8080" protocol="HTTP/1.1"
           maxThreads="100"
           minSpareThreads="5"
           maxSpareThreads="20" />
线程池性能对比
配置 最大线程数 吞吐量 (req/s) 平均响应时间 (ms) 线程使用率 (%)
默认配置 200 8,000 12 85
高并发配置 500 12,000 8 70
低延迟配置 100 6,000 15 95

10.3 高并发场景模拟

10.3.1 高并发测试场景

场景1:突发流量测试
bash 复制代码
#!/bin/bash
# burst_traffic_test.sh

# 模拟突发流量
echo "开始突发流量测试..."

# 阶段1:正常流量 (100 req/s)
echo "阶段1:正常流量"
./wrk -t10 -c100 -d30s http://localhost:8080/myapp &

# 等待30秒
sleep 30

# 阶段2:突发流量 (1000 req/s)
echo "阶段2:突发流量"
./wrk -t50 -c500 -d10s http://localhost:8080/myapp &

# 等待10秒
sleep 10

# 阶段3:恢复正常流量
echo "阶段3:恢复正常流量"
./wrk -t10 -c100 -d30s http://localhost:8080/myapp

echo "突发流量测试完成"
场景2:持续高并发测试
bash 复制代码
#!/bin/bash
# sustained_high_concurrency_test.sh

# 持续高并发测试
echo "开始持续高并发测试..."

# 测试参数
THREADS=200
CONNECTIONS=2000
DURATION=300s

# 执行测试
./wrk -t$THREADS -c$CONNECTIONS -d$DURATION \
      -H "Connection: keep-alive" \
      -H "Accept-Encoding: gzip,deflate" \
      http://localhost:8080/myapp > sustained_test_results.txt

echo "持续高并发测试完成"

10.3.2 内存压力测试

内存压力测试脚本
java 复制代码
// 内存压力测试 Servlet
@WebServlet("/memory-test")
public class MemoryTestServlet extends HttpServlet {
    
    private final List<byte[]> memoryBlocks = new ArrayList<>();
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // 获取内存大小参数
        String sizeParam = request.getParameter("size");
        int size = sizeParam != null ? Integer.parseInt(sizeParam) : 1024;
        
        // 分配内存块
        byte[] memoryBlock = new byte[size * 1024]; // KB
        Arrays.fill(memoryBlock, (byte) 1);
        memoryBlocks.add(memoryBlock);
        
        // 返回内存使用情况
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        
        response.setContentType("application/json");
        response.getWriter().write(String.format(
            "{\"allocated\": %d, \"heapUsed\": %d, \"heapMax\": %d, \"blocks\": %d}",
            memoryBlocks.size() * size,
            heapUsage.getUsed(),
            heapUsage.getMax(),
            memoryBlocks.size()
        ));
    }
}
内存压力测试执行
bash 复制代码
#!/bin/bash
# memory_pressure_test.sh

echo "开始内存压力测试..."

# 测试不同内存分配大小
for size in 1 10 100 1000; do
    echo "测试内存分配大小: ${size}KB"
    
    # 执行测试
    ./wrk -t50 -c100 -d60s \
          "http://localhost:8080/memory-test?size=$size" \
          > memory_test_${size}kb.txt
    
    # 等待内存回收
    sleep 10
done

echo "内存压力测试完成"

10.4 多节点集群配置

10.4.1 集群架构设计

集群架构图

数据层 Tomcat 集群 负载均衡层 Redis 集群 MySQL 主从 Tomcat 1 Tomcat 2 Tomcat 3 Nginx

集群配置
xml 复制代码
<!-- server.xml 集群配置 -->
<Server port="8005" shutdown="SHUTDOWN">
    <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        
        <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
            <!-- 集群配置 -->
            <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                     channelSendOptions="8">
                
                <!-- 集群管理器 -->
                <Manager className="org.apache.catalina.ha.session.DeltaManager"
                         expireSessionsOnShutdown="false"
                         notifyListenersOnReplication="true" />
                
                <!-- 集群通道 -->
                <Channel className="org.apache.catalina.tribes.group.GroupChannel">
                    <Membership className="org.apache.catalina.tribes.membership.McastService"
                                address="228.0.0.4"
                                port="45564"
                                frequency="500"
                                dropTime="3000" />
                    
                    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                               address="auto"
                               port="4000"
                               autoBind="100"
                               selectorTimeout="5000"
                               maxThreads="6" />
                    
                    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
                        <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"
                                   timeout="60000"
                                   maxActive="10"
                                   maxIdle="5" />
                    </Sender>
                    
                    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
                    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor" />
                </Channel>
                
                <!-- 集群阀门 -->
                <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                       filter=".*\.gif|.*\.js|.*\.jpg|.*\.png|.*\.htm|.*\.html|.*\.css|.*\.txt" />
                
                <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                          tempDir="/tmp/war-temp/"
                          deployDir="/tmp/war-deploy/"
                          watchDir="/tmp/war-listen/"
                          watchEnabled="false" />
                
                <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener" />
                <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
            </Cluster>
            
            <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
                <Context path="/myapp" docBase="myapp" />
            </Host>
        </Engine>
    </Service>
</Server>

10.4.2 负载均衡配置

Nginx 负载均衡配置
nginx 复制代码
# nginx.conf
upstream tomcat_cluster {
    server 192.168.1.10:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
    
    # 健康检查
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://tomcat_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 连接配置
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
        
        # 缓冲配置
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;
        
        # 缓存配置
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
    }
    
    # 健康检查端点
    location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
}

10.4.3 Session 复制配置

Session 复制实现
java 复制代码
// Session 复制监听器
public class SessionReplicationListener implements HttpSessionListener {
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("Session created: " + session.getId());
        
        // 设置 Session 属性
        session.setAttribute("createdTime", System.currentTimeMillis());
        session.setAttribute("serverId", getServerId());
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("Session destroyed: " + session.getId());
    }
    
    private String getServerId() {
        return System.getProperty("server.id", "unknown");
    }
}
Session 复制测试
bash 复制代码
#!/bin/bash
# session_replication_test.sh

echo "开始 Session 复制测试..."

# 测试 Session 复制
for i in {1..10}; do
    echo "测试轮次: $i"
    
    # 创建 Session
    SESSION_ID=$(curl -s -c cookies.txt http://localhost:8080/myapp/session-test)
    echo "创建 Session: $SESSION_ID"
    
    # 验证 Session 复制
    for server in 192.168.1.10:8080 192.168.1.11:8080 192.168.1.12:8080; do
        echo "验证服务器: $server"
        curl -s -b cookies.txt http://$server/myapp/session-test
    done
    
    sleep 5
done

echo "Session 复制测试完成"

10.5 生产环境调优模板

10.5.1 生产环境配置模板

server.xml 生产配置
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
    <!-- 全局监听器 -->
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
    
    <!-- 全局资源 -->
    <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>
    
    <!-- 服务配置 -->
    <Service name="Catalina">
        <!-- HTTP 连接器 -->
        <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
                   connectionTimeout="20000"
                   redirectPort="8443"
                   maxThreads="500"
                   minSpareThreads="50"
                   maxSpareThreads="100"
                   acceptCount="200"
                   maxConnections="8192"
                   keepAliveTimeout="60000"
                   maxKeepAliveRequests="100"
                   compression="on"
                   compressionMinSize="2048"
                   compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json"
                   useSendfile="true"
                   URIEncoding="UTF-8"
                   server="Apache-Coyote/1.1"
                   maxHttpHeaderSize="8192"
                   maxPostSize="2097152"
                   maxParameterCount="10000"
                   maxSwallowSize="2097152" />
        
        <!-- AJP 连接器 -->
        <Connector port="8009" protocol="AJP/1.3"
                   redirectPort="8443"
                   maxThreads="500"
                   minSpareThreads="50"
                   maxSpareThreads="100"
                   acceptCount="200" />
        
        <!-- 引擎配置 -->
        <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
            <!-- 集群配置 -->
            <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                     channelSendOptions="8">
                <Manager className="org.apache.catalina.ha.session.DeltaManager"
                         expireSessionsOnShutdown="false"
                         notifyListenersOnReplication="true" />
                <Channel className="org.apache.catalina.tribes.group.GroupChannel">
                    <Membership className="org.apache.catalina.tribes.membership.McastService"
                                address="228.0.0.4"
                                port="45564"
                                frequency="500"
                                dropTime="3000" />
                    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                               address="auto"
                               port="4000"
                               autoBind="100"
                               selectorTimeout="5000"
                               maxThreads="6" />
                    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
                        <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"
                                   timeout="60000"
                                   maxActive="10"
                                   maxIdle="5" />
                    </Sender>
                    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
                    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor" />
                </Channel>
                <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                       filter=".*\.gif|.*\.js|.*\.jpg|.*\.png|.*\.htm|.*\.html|.*\.css|.*\.txt" />
                <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                          tempDir="/tmp/war-temp/"
                          deployDir="/tmp/war-deploy/"
                          watchDir="/tmp/war-listen/"
                          watchEnabled="false" />
                <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener" />
                <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
            </Cluster>
            
            <!-- 主机配置 -->
            <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="false"
                  xmlValidation="false" xmlNamespaceAware="false"
                  deployOnStartup="true">
                
                <!-- 应用上下文 -->
                <Context path="/myapp" docBase="myapp" reloadable="false">
                    <!-- 数据源配置 -->
                    <Resource name="jdbc/MyDB" auth="Container"
                              type="javax.sql.DataSource"
                              driverClassName="com.mysql.jdbc.Driver"
                              url="jdbc:mysql://localhost:3306/mydb"
                              username="user"
                              password="pass"
                              maxTotal="20"
                              maxIdle="10"
                              maxWaitMillis="10000"
                              validationQuery="SELECT 1"
                              testOnBorrow="true"
                              testWhileIdle="true"
                              timeBetweenEvictionRunsMillis="30000"
                              minEvictableIdleTimeMillis="60000" />
                    
                    <!-- 环境变量 -->
                    <Environment name="app.config" value="production" type="java.lang.String" />
                    <Environment name="app.version" value="1.0.0" type="java.lang.String" />
                </Context>
                
                <!-- 访问日志 -->
                <Valve className="org.apache.catalina.valves.AccessLogValve"
                       directory="logs"
                       prefix="localhost_access_log"
                       suffix=".txt"
                       pattern="%h %l %u %t &quot;%r&quot; %s %b %D"
                       resolveHosts="false" />
                
                <!-- 错误报告 -->
                <Valve className="org.apache.catalina.valves.ErrorReportValve"
                       showReport="false"
                       showServerInfo="false" />
            </Host>
        </Engine>
    </Service>
</Server>

10.5.2 JVM 调优模板

生产环境 JVM 参数
bash 复制代码
#!/bin/bash
# production_jvm_opts.sh

# 内存配置
export JAVA_OPTS="-Xms2g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

# GC 配置
export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m"

# 性能配置
export JAVA_OPTS="$JAVA_OPTS -XX:+UseStringDeduplication -XX:+OptimizeStringConcat"

# 监控配置
export JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime"

# 安全配置
export JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"

# 网络配置
export JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true"

# 时区配置
export JAVA_OPTS="$JAVA_OPTS -Duser.timezone=Asia/Shanghai"

# 字符集配置
export JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"

echo "JVM 参数配置完成: $JAVA_OPTS"

10.5.3 系统调优模板

系统参数优化
bash 复制代码
#!/bin/bash
# system_optimization.sh

echo "开始系统参数优化..."

# 文件句柄数优化
echo "* soft nofile 65536" >> /etc/security/limits.conf
echo "* hard nofile 65536" >> /etc/security/limits.conf

# 网络参数优化
cat >> /etc/sysctl.conf << EOF
# 网络参数优化
net.core.somaxconn = 65536
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65536
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_congestion_control = bbr

# 内存参数优化
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.overcommit_memory = 1

# 文件系统参数优化
fs.file-max = 2097152
EOF

# 应用参数
sysctl -p

echo "系统参数优化完成"

10.6 实战案例分析

10.6.1 电商系统性能优化案例

案例背景
  • 系统规模:日活用户 100万,峰值 QPS 10万
  • 技术栈:Spring Boot + Tomcat + MySQL + Redis
  • 性能问题:高峰期响应时间超过 2秒,系统不稳定
问题分析
java 复制代码
// 性能问题分析
public class PerformanceAnalysis {
    
    public void analyzePerformanceIssues() {
        // 1. 线程池配置问题
        System.out.println("问题1:线程池配置不当");
        System.out.println("- 最大线程数:200(过少)");
        System.out.println("- 队列长度:100(过短)");
        System.out.println("- 建议:最大线程数 500,队列长度 200");
        
        // 2. 数据库连接池问题
        System.out.println("问题2:数据库连接池配置不当");
        System.out.println("- 最大连接数:10(过少)");
        System.out.println("- 连接超时:30秒(过长)");
        System.out.println("- 建议:最大连接数 50,连接超时 10秒");
        
        // 3. 缓存策略问题
        System.out.println("问题3:缓存策略不当");
        System.out.println("- 缓存命中率:60%(过低)");
        System.out.println("- 缓存过期时间:1小时(过短)");
        System.out.println("- 建议:优化缓存策略,延长过期时间");
        
        // 4. JVM 配置问题
        System.out.println("问题4:JVM 配置不当");
        System.out.println("- 堆内存:1GB(过少)");
        System.out.println("- GC 策略:ParallelGC(不适合)");
        System.out.println("- 建议:堆内存 4GB,使用 G1GC");
    }
}
优化方案
xml 复制代码
<!-- 优化后的 server.xml 配置 -->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="10000"
           redirectPort="8443"
           maxThreads="500"
           minSpareThreads="50"
           maxSpareThreads="100"
           acceptCount="200"
           maxConnections="8192"
           keepAliveTimeout="60000"
           maxKeepAliveRequests="100"
           compression="on"
           compressionMinSize="1024"
           compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json"
           useSendfile="true"
           URIEncoding="UTF-8" />
优化效果
指标 优化前 优化后 提升幅度
平均响应时间 2000ms 200ms 90%
峰值 QPS 5,000 15,000 200%
错误率 5% 0.1% 98%
CPU 使用率 90% 60% 33%
内存使用率 95% 70% 26%

10.6.2 金融系统高可用案例

案例背景
  • 系统规模:日交易量 1000万笔,峰值 TPS 5万
  • 技术栈:Spring Boot + Tomcat + Oracle + Redis 集群
  • 可用性要求:99.99%,RTO < 5分钟,RPO < 1分钟
高可用架构
yaml 复制代码
# docker-compose.yml
version: '3.8'
services:
  # 负载均衡器
  nginx:
    image: nginx:1.20
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - tomcat1
      - tomcat2
      - tomcat3
  
  # Tomcat 集群
  tomcat1:
    image: tomcat:9.0
    ports:
      - "8081:8080"
    environment:
      - SERVER_ID=tomcat1
      - CLUSTER_ADDRESS=228.0.0.4
      - CLUSTER_PORT=45564
    volumes:
      - ./server1.xml:/usr/local/tomcat/conf/server.xml
      - ./app.war:/usr/local/tomcat/webapps/app.war
  
  tomcat2:
    image: tomcat:9.0
    ports:
      - "8082:8080"
    environment:
      - SERVER_ID=tomcat2
      - CLUSTER_ADDRESS=228.0.0.4
      - CLUSTER_PORT=45564
    volumes:
      - ./server2.xml:/usr/local/tomcat/conf/server.xml
      - ./app.war:/usr/local/tomcat/webapps/app.war
  
  tomcat3:
    image: tomcat:9.0
    ports:
      - "8083:8080"
    environment:
      - SERVER_ID=tomcat3
      - CLUSTER_ADDRESS=228.0.0.4
      - CLUSTER_PORT=45564
    volumes:
      - ./server3.xml:/usr/local/tomcat/conf/server.xml
      - ./app.war:/usr/local/tomcat/webapps/app.war
  
  # Redis 集群
  redis-master:
    image: redis:6.0
    ports:
      - "6379:6379"
    command: redis-server --appendonly yes
  
  redis-slave1:
    image: redis:6.0
    ports:
      - "6380:6379"
    command: redis-server --slaveof redis-master 6379 --appendonly yes
  
  redis-slave2:
    image: redis:6.0
    ports:
      - "6381:6379"
    command: redis-server --slaveof redis-master 6379 --appendonly yes
监控告警配置
yaml 复制代码
# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'tomcat-cluster'
    static_configs:
      - targets: ['tomcat1:8080', 'tomcat2:8080', 'tomcat3:8080']
    metrics_path: '/actuator/prometheus'
    scrape_interval: 5s
  
  - job_name: 'redis-cluster'
    static_configs:
      - targets: ['redis-master:6379', 'redis-slave1:6380', 'redis-slave2:6381']
    scrape_interval: 10s

# 告警规则
rule_files:
  - "alert_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

10.6.3 微服务架构优化案例

案例背景
  • 系统规模:50个微服务,日请求量 5000万
  • 技术栈:Spring Cloud + Tomcat + Consul + Kafka
  • 性能问题:服务间调用延迟高,资源利用率低
微服务优化策略
java 复制代码
// 微服务性能优化
@Configuration
public class MicroserviceOptimization {
    
    @Bean
    public TomcatEmbeddedServletContainerFactory tomcatFactory() {
        return new TomcatEmbeddedServletContainerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                // 1. 连接池优化
                context.addParameter("maxConnections", "1000");
                context.addParameter("maxThreads", "200");
                
                // 2. 缓存优化
                context.addParameter("cacheSize", "10000");
                context.addParameter("cacheTTL", "3600");
                
                // 3. 压缩优化
                context.addParameter("compression", "on");
                context.addParameter("compressionMinSize", "1024");
            }
        };
    }
    
    @Bean
    public RestTemplate restTemplate() {
        // HTTP 连接池配置
        HttpComponentsClientHttpRequestFactory factory = 
            new HttpComponentsClientHttpRequestFactory();
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(10000);
        factory.setConnectionRequestTimeout(3000);
        
        // 连接池配置
        PoolingHttpClientConnectionManager connectionManager = 
            new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(200);
        connectionManager.setDefaultMaxPerRoute(50);
        
        CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();
        
        factory.setHttpClient(httpClient);
        
        return new RestTemplate(factory);
    }
}
优化效果对比
指标 优化前 优化后 提升幅度
服务调用延迟 500ms 100ms 80%
资源利用率 40% 70% 75%
错误率 2% 0.5% 75%
吞吐量 1000 req/s 3000 req/s 200%

10.7 本章小结

关键要点

  1. 压测工具使用

    • JMeter 功能强大,适合复杂场景测试
    • wrk 轻量级,适合快速性能测试
    • Apache Bench 简单易用,适合基础测试
  2. 性能对比测试

    • 不同 I/O 模式性能差异明显
    • 线程池配置对性能影响巨大
    • 合理配置可以显著提升性能
  3. 高并发场景模拟

    • 突发流量测试验证系统弹性
    • 持续高并发测试验证系统稳定性
    • 内存压力测试验证系统健壮性
  4. 多节点集群配置

    • 集群架构提供高可用性
    • 负载均衡分散请求压力
    • Session 复制保证数据一致性
  5. 生产环境调优

    • 系统参数优化提升基础性能
    • JVM 调优减少 GC 影响
    • 应用配置优化提升业务性能
  6. 实战案例分析

    • 电商系统优化案例
    • 金融系统高可用案例
    • 微服务架构优化案例

最佳实践

  1. 性能测试策略

    • 建立完整的性能测试体系
    • 定期执行性能回归测试
    • 监控关键性能指标
  2. 优化策略

    • 从系统层面到应用层面全面优化
    • 根据业务特点选择合适的优化方案
    • 持续监控和调优
  3. 高可用设计

    • 设计容错和恢复机制
    • 实现自动化运维
    • 建立完善的监控告警体系

学习建议

  1. 实践练习

    • 搭建测试环境进行性能测试
    • 尝试不同的优化方案
    • 分析测试结果和优化效果
  2. 持续学习

    • 关注最新的性能优化技术
    • 学习其他系统的优化经验
    • 参与开源项目的性能优化
  3. 经验积累

    • 记录优化过程和效果
    • 总结优化经验和教训
    • 分享优化心得和技巧

相关资源

相关推荐
u0103055276 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055276 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝8 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055278 天前
Java图像处理实战指南
人工智能·1024程序员节
u0103055278 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u01030552711 天前
Java实用类应用精讲
人工智能·1024程序员节
u01030552711 天前
Java I/O核心操作实战
人工智能·1024程序员节
u01030552712 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节
u01030552713 天前
Java模块化编程实战指南
人工智能·笔记·1024程序员节
u01030552714 天前
使用BufferedReader读取控制台输入
人工智能·1024程序员节