🚀 第十章: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 "%r" %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 本章小结
关键要点
-
压测工具使用:
- JMeter 功能强大,适合复杂场景测试
- wrk 轻量级,适合快速性能测试
- Apache Bench 简单易用,适合基础测试
-
性能对比测试:
- 不同 I/O 模式性能差异明显
- 线程池配置对性能影响巨大
- 合理配置可以显著提升性能
-
高并发场景模拟:
- 突发流量测试验证系统弹性
- 持续高并发测试验证系统稳定性
- 内存压力测试验证系统健壮性
-
多节点集群配置:
- 集群架构提供高可用性
- 负载均衡分散请求压力
- Session 复制保证数据一致性
-
生产环境调优:
- 系统参数优化提升基础性能
- JVM 调优减少 GC 影响
- 应用配置优化提升业务性能
-
实战案例分析:
- 电商系统优化案例
- 金融系统高可用案例
- 微服务架构优化案例
最佳实践
-
性能测试策略:
- 建立完整的性能测试体系
- 定期执行性能回归测试
- 监控关键性能指标
-
优化策略:
- 从系统层面到应用层面全面优化
- 根据业务特点选择合适的优化方案
- 持续监控和调优
-
高可用设计:
- 设计容错和恢复机制
- 实现自动化运维
- 建立完善的监控告警体系
学习建议
-
实践练习:
- 搭建测试环境进行性能测试
- 尝试不同的优化方案
- 分析测试结果和优化效果
-
持续学习:
- 关注最新的性能优化技术
- 学习其他系统的优化经验
- 参与开源项目的性能优化
-
经验积累:
- 记录优化过程和效果
- 总结优化经验和教训
- 分享优化心得和技巧
相关资源: