Apache Tomcat,轻量级Java Web应用服务器
在企业级Java Web应用部署领域,Apache Tomcat作为Servlet容器的事实标准,已经成为承载Java Web应用的中流砥柱。这个由Apache软件基金会维护的开源Web服务器和Servlet容器,通过其轻量级架构和优秀的性能表现,为无数Java Web应用提供了可靠的运行环境。在电商网站中,Tomcat处理着每秒数千的并发请求;在企业内部系统中,它稳定运行着业务管理平台;在微服务架构中,它作为独立的服务容器承载着各个业务模块。从传统的JSP应用部署到现代化的Spring Boot应用运行,从Servlet API的实现到WebSocket的支持,Tomcat都在幕后为Java Web生态的繁荣提供着坚实的基础设施。
核心架构与工作原理
1. 容器层次结构与组件模型
Tomcat的核心架构基于分层的容器模型,每个层次都有明确的职责。
xml
<!-- server.xml - Tomcat主配置文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<!-- 全局JNDI资源配置 -->
<GlobalNamingResources>
<Resource name="UserDatabase"
auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- 服务定义 -->
<Service name="Catalina">
<!-- 连接器配置 - HTTP/1.1 -->
<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="200"
minSpareThreads="10"
acceptCount="100"
maxConnections="10000"
compression="on"
compressionMinSize="1024"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/json"
redirectPort="8443" />
<!-- 连接器配置 - AJP(用于与Apache HTTP Server集成) -->
<Connector port="8009"
protocol="AJP/1.3"
redirectPort="8443" />
<!-- 连接器配置 - HTTPS -->
<Connector port="8443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150"
SSLEnabled="true">
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/keystore.jks"
certificateKeystorePassword="changeit"
type="RSA" />
</SSLHostConfig>
</Connector>
<!-- 引擎配置 -->
<Engine name="Catalina" defaultHost="localhost">
<!-- 集群配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<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"/>
</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=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
</Cluster>
<!-- 主机配置 -->
<Host name="localhost"
appBase="webapps"
unpackWARs="true"
autoDeploy="true"
deployOnStartup="true">
<!-- 上下文配置(Web应用配置) -->
<Context path="/ecommerce"
docBase="/opt/apps/ecommerce.war"
reloadable="false"
cachingAllowed="true"
crossContext="true">
<!-- 数据源配置 -->
<Resource name="jdbc/EcommerceDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/ecommerce?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai"
username="ecommerce_user"
password="${db.password}"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"
validationQuery="SELECT 1"
testOnBorrow="true"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"
minEvictableIdleTimeMillis="60000"
removeAbandoned="true"
removeAbandonedTimeout="60"
logAbandoned="true" />
<!-- JNDI资源配置 -->
<Resource name="mail/Session"
auth="Container"
type="javax.mail.Session"
mail.smtp.host="smtp.example.com"
mail.smtp.port="587"
mail.smtp.auth="true"
mail.smtp.starttls.enable="true"
mail.smtp.username="${mail.username}"
mail.smtp.password="${mail.password}" />
<!-- 环境变量 -->
<Environment name="app.env"
value="production"
type="java.lang.String"
override="false" />
<!-- 监听器 -->
<Listener className="com.example.StartupListener" />
<!-- 过滤器 -->
<Filter name="LoggingFilter"
className="com.example.LoggingFilter">
<init-param>
<param-name>logLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
</Filter>
<Filter-mapping>
<Filter-name>LoggingFilter</Filter-name>
<url-pattern>/*</url-pattern>
</Filter-mapping>
</Context>
<!-- 虚拟主机配置 -->
<Alias>www.example.com</Alias>
<Alias>example.com</Alias>
<!-- 访问日志阀门 -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="%h %l %u %t "%r" %s %b %D"
rotatable="true"
renameOnRotate="true"
fileDateFormat="yyyy-MM-dd" />
<!-- 单点登录阀门 -->
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
</Host>
<!-- 其他虚拟主机 -->
<Host name="api.example.com"
appBase="webapps-api"
unpackWARs="true"
autoDeploy="true">
<Alias>api.internal.com</Alias>
</Host>
</Engine>
</Service>
</Server>
2. 连接器配置优化
xml
<!-- server.xml中的高性能连接器配置 -->
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
<!-- 线程池配置 -->
executor="tomcatThreadPool"
maxThreads="500"
minSpareThreads="20"
acceptCount="1000"
<!-- 连接配置 -->
connectionTimeout="30000"
keepAliveTimeout="15000"
maxKeepAliveRequests="100"
maxConnections="10000"
<!-- 缓冲区配置 -->
socketBuffer="8192"
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/json,application/javascript"
<!-- SSL配置 -->
SSLEnabled="false"
<!-- 其他优化 -->
enableLookups="false"
disableUploadTimeout="true"
connectionUploadTimeout="300000"
maxHttpHeaderSize="8192"
maxPostSize="10485760"
<!-- URI编码 -->
URIEncoding="UTF-8"
<!-- 重定向 -->
redirectPort="8443"
<!-- 性能优化参数 -->
socket.rxBufSize="25188"
socket.txBufSize="43800"
socket.appReadBufSize="1024"
socket.appWriteBufSize="1024"
<!-- 使用sendfile特性 -->
useSendfile="true"
<!-- 限制请求头大小 -->
maxHeaderCount="100"
maxTrailerCount="10" />
<!-- 线程池配置 -->
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="500"
minSpareThreads="20"
maxIdleTime="60000"
prestartminSpareThreads="true" />
高级特性与应用
1. 集群与Session复制配置
xml
<!-- 集群配置 - 多节点Session共享 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">
<!-- Manager配置 -->
<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="5000"
selectorTimeout="100"
maxThreads="6"/>
<!-- 发送器 -->
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<!-- 拦截器 -->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
</Channel>
<!-- 集群阀门 -->
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt"/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<!-- 集群监听器 -->
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
<!-- Web应用中的集群配置 -->
<distributable />
2. 自定义阀门与过滤器
java
// 自定义访问阀门 - 记录详细的访问日志
public class DetailedAccessLogValve extends ValveBase {
private static final Logger logger = LoggerFactory.getLogger(DetailedAccessLogValve.class);
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
try {
// 执行下一个阀门
getNext().invoke(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
logAccess(request, response, duration);
}
}
private void logAccess(Request request, Response response, long duration) {
HttpServletRequest httpRequest = request.getRequest();
HttpServletResponse httpResponse = (HttpServletResponse) response;
StringBuilder logMessage = new StringBuilder();
logMessage.append("RemoteAddr: ").append(httpRequest.getRemoteAddr())
.append(" | RemoteHost: ").append(httpRequest.getRemoteHost())
.append(" | Method: ").append(httpRequest.getMethod())
.append(" | URI: ").append(httpRequest.getRequestURI())
.append(" | QueryString: ").append(httpRequest.getQueryString())
.append(" | Protocol: ").append(httpRequest.getProtocol())
.append(" | Status: ").append(httpResponse.getStatus())
.append(" | ContentLength: ").append(response.getContentLength())
.append(" | Duration: ").append(duration).append("ms")
.append(" | UserAgent: ").append(httpRequest.getHeader("User-Agent"))
.append(" | Referer: ").append(httpRequest.getHeader("Referer"))
.append(" | SessionId: ").append(httpRequest.getRequestedSessionId());
// 记录业务特定的信息
if (httpRequest.getAttribute("userId") != null) {
logMessage.append(" | UserId: ").append(httpRequest.getAttribute("userId"));
}
// 根据状态码记录不同级别的日志
int status = httpResponse.getStatus();
if (status >= 500) {
logger.error(logMessage.toString());
} else if (status >= 400) {
logger.warn(logMessage.toString());
} else {
logger.info(logMessage.toString());
}
}
}
// 自定义过滤器 - 请求/响应处理
@WebFilter(filterName = "PerformanceFilter", urlPatterns = "/*")
public class PerformanceFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(PerformanceFilter.class);
private static final ThreadLocal<Long> startTimeHolder = new ThreadLocal<>();
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 记录开始时间
startTimeHolder.set(System.currentTimeMillis());
// 添加响应头
httpResponse.setHeader("X-Request-ID", UUID.randomUUID().toString());
// 包装响应以捕获响应体大小
ContentCachingResponseWrapper wrappedResponse =
new ContentCachingResponseWrapper(httpResponse);
try {
chain.doFilter(request, wrappedResponse);
} finally {
// 计算处理时间
long duration = System.currentTimeMillis() - startTimeHolder.get();
startTimeHolder.remove();
// 记录性能指标
logPerformance(httpRequest, wrappedResponse, duration);
// 复制响应内容
wrappedResponse.copyBodyToResponse();
}
}
private void logPerformance(HttpServletRequest request,
ContentCachingResponseWrapper response,
long duration) {
// 只记录超过阈值的请求
if (duration > 1000) { // 1秒
logger.warn("慢请求检测: {} {}, 耗时: {}ms, 状态: {}, 响应大小: {}",
request.getMethod(), request.getRequestURI(),
duration, response.getStatus(),
response.getContentSize());
}
// 记录到指标系统
Metrics.recordRequest(request.getRequestURI(), duration, response.getStatus());
}
}
// 自定义监听器 - 应用生命周期管理
@WebListener
public class ApplicationLifecycleListener implements ServletContextListener {
private static final Logger logger = LoggerFactory.getLogger(ApplicationLifecycleListener.class);
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
String appName = context.getContextPath();
logger.info("应用 {} 开始启动...", appName);
try {
// 初始化应用配置
initApplicationConfig(context);
// 预热缓存
warmupCaches();
// 连接池预热
warmupConnectionPools();
logger.info("应用 {} 启动完成", appName);
} catch (Exception e) {
logger.error("应用 {} 启动失败", appName, e);
throw new RuntimeException("应用启动失败", e);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
String appName = sce.getServletContext().getContextPath();
logger.info("应用 {} 开始关闭...", appName);
try {
// 清理资源
cleanupResources();
// 等待处理中的请求完成
waitForPendingRequests();
logger.info("应用 {} 关闭完成", appName);
} catch (Exception e) {
logger.error("应用 {} 关闭异常", appName, e);
}
}
private void initApplicationConfig(ServletContext context) {
// 从环境变量或配置文件加载配置
String env = System.getProperty("app.env", "development");
context.setAttribute("app.env", env);
// 加载数据库配置
Properties dbProps = loadProperties("database.properties");
context.setAttribute("db.config", dbProps);
}
private void warmupCaches() {
// 预热常用数据缓存
CacheManager.getInstance().warmup();
}
private void warmupConnectionPools() {
// 预热数据库连接池
DataSource dataSource = (DataSource) InitialContext.doLookup("java:comp/env/jdbc/AppDB");
try (Connection conn = dataSource.getConnection()) {
// 执行简单的查询来建立连接
try (Statement stmt = conn.createStatement()) {
stmt.execute("SELECT 1");
}
} catch (Exception e) {
logger.warn("连接池预热失败", e);
}
}
}
实战案例:电商平台Tomcat部署架构
下面通过一个完整的电商平台案例,展示Tomcat在生产环境中的高级应用。
xml
<!-- 生产环境Tomcat配置 - 电商平台 -->
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<!-- 全局资源 -->
<GlobalNamingResources>
<!-- JNDI数据源 -->
<Resource name="jdbc/EcommerceDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://db-cluster:3306/ecommerce?useSSL=true&serverTimezone=UTC"
username="${db.user}"
password="${db.pass}"
initialSize="10"
maxTotal="100"
maxIdle="50"
minIdle="10"
maxWaitMillis="10000"
validationQuery="SELECT 1"
testOnBorrow="true"
testOnReturn="false"
testWhileIdle="true"
timeBetweenEvictionRunsMillis="30000"
minEvictableIdleTimeMillis="60000"
removeAbandoned="true"
removeAbandonedTimeout="300"
logAbandoned="true"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer" />
<!-- Redis连接池 -->
<Resource name="redis/Default"
auth="Container"
type="redis.clients.jedis.JedisPool"
factory="com.example.RedisPoolFactory"
host="${redis.host}"
port="${redis.port}"
password="${redis.pass}"
maxTotal="100"
maxIdle="50"
minIdle="10"
testOnBorrow="true" />
</GlobalNamingResources>
<Service name="Catalina">
<!-- HTTP连接器 - 面向用户流量 -->
<Connector port="80"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
executor="userThreadPool"
maxThreads="1000"
minSpareThreads="100"
acceptCount="2000"
connectionTimeout="30000"
keepAliveTimeout="15000"
maxKeepAliveRequests="100"
compression="on"
compressionMinSize="1024"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/json"
enableLookups="false"
URIEncoding="UTF-8"
server="Tomcat-Ecommerce" />
<!-- 内部API连接器 - 分离内部服务流量 -->
<Connector port="8081"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
executor="apiThreadPool"
maxThreads="500"
minSpareThreads="50"
acceptCount="1000"
connectionTimeout="10000"
keepAliveTimeout="30000"
maxKeepAliveRequests="-1"
enableLookups="false"
URIEncoding="UTF-8"
address="10.0.0.10" /> <!-- 绑定内部IP -->
<!-- AJP连接器 - 与Nginx集成 -->
<Connector port="8009"
protocol="AJP/1.3"
secretRequired="true"
secret="${ajp.secret}"
maxThreads="500"
connectionTimeout="30000"
keepAliveTimeout="15000"
packetSize="8192" />
<!-- 线程池配置 -->
<Executor name="userThreadPool"
namePrefix="user-exec-"
maxThreads="1000"
minSpareThreads="100"
maxIdleTime="60000"
prestartminSpareThreads="true" />
<Executor name="apiThreadPool"
namePrefix="api-exec-"
maxThreads="500"
minSpareThreads="50"
maxIdleTime="60000"
prestartminSpareThreads="true" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">
<!-- 集群配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
processExpiresFrequency="6"
maxActiveSessions="10000" />
<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="10.0.0.10"
port="5000"
selectorTimeout="100"
maxThreads="6" />
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.(gif|jpg|png|ico|css|js|woff|woff2|ttf)$" />
</Cluster>
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="false">
<!-- 主电商应用 -->
<Context path=""
docBase="/opt/apps/ecommerce-frontend"
reloadable="false"
cachingAllowed="true"
distribute="true">
<!-- 监控阀门 -->
<Valve className="org.apache.catalina.valves.StuckThreadDetectionValve"
threshold="30000" /> <!-- 30秒 -->
<!-- 访问日志 -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/var/log/tomcat"
prefix="ecommerce_access"
suffix=".log"
pattern="%{X-Forwarded-For}i %l %u %t "%r" %s %b %D "%{Referer}i" "%{User-Agent}i""
rotatable="true"
renameOnRotate="true"
fileDateFormat="yyyy-MM-dd" />
<!-- 请求转储阀门(仅用于调试) -->
<!-- <Valve className="org.apache.catalina.valves.RequestDumperValve" /> -->
</Context>
<!-- 管理后台 -->
<Context path="/admin"
docBase="/opt/apps/ecommerce-admin"
reloadable="false"
privileged="true">
<!-- 安全约束 -->
<SecurityConstraint>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</SecurityConstraint>
<LoginConfig>
<auth-method>BASIC</auth-method>
<realm-name>Admin Console</realm-name>
</LoginConfig>
</Context>
<!-- 静态资源服务器 -->
<Context path="/static"
docBase="/opt/static"
crossContext="true"
reloadable="false">
<!-- 静态资源缓存控制 -->
<Resources className="org.apache.catalina.webresources.StandardRoot"
cacheMaxSize="104857600" /> <!-- 100MB缓存 -->
<!-- 默认Servlet配置 -->
<DefaultServlet>
<init-param>
<param-name>fileEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
</DefaultServlet>
</Context>
</Host>
</Engine>
</Service>
</Server>
JVM优化配置
bash
# catalina.sh或setenv.sh中的JVM参数配置 #!/bin/bash # 内存配置 export CATALINA_OPTS="$CATALINA_OPTS -Xms4g -Xmx4g" export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m" export CATALINA_OPTS="$CATALINA_OPTS -Xmn2g" # 年轻代大小 # GC优化(使用G1垃圾收集器) 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:InitiatingHeapOccupancyPercent=35" export CATALINA_OPTS="$CATALINA_OPTS -XX:+ParallelRefProcEnabled" # 性能监控 export CATALINA_OPTS="$CATALINA_OPTS -XX:+HeapDumpOnOutOfMemoryError" export CATALINA_OPTS="$CATALINA_OPTS -XX:HeapDumpPath=/opt/tomcat/logs/heapdump.hprof" export CATALINA_OPTS="$CATALINA_OPTS -XX:ErrorFile=/opt/tomcat/logs/hs_err_pid%p.log" # 调试和监控 export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote" export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=9010" export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false" export CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false" # 应用特定配置 export CATALINA_OPTS="$CATALINA_OPTS -Dapp.env=production" export CATALINA_OPTS="$CATALINA_OPTS -Dlog.path=/var/log/tomcat" export CATALINA_OPTS="$CATALINA_OPTS -Djava.security.egd=file:/dev/./urandom" # 字符集 export LANG="en_US.UTF-8" export LC_ALL="en_US.UTF-8" # 文件描述符限制(在系统层面设置) ulimit -n 65535 ulimit -u 4096
高级应用场景
1. 基于Nginx的负载均衡配置
nginx
# nginx.conf - 负载均衡配置
upstream tomcat_cluster {
# 负载均衡算法
least_conn; # 最少连接数
# 会话保持
sticky cookie srv_id expires=1h domain=.example.com path=/;
# 服务器节点
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.13:8080 max_fails=3 fail_timeout=30s backup; # 备用节点
# 健康检查
health_check interval=5s fails=3 passes=2;
}
server {
listen 80;
server_name www.example.com;
# 静态文件服务
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf)$ {
root /opt/static;
expires 30d;
access_log off;
add_header Cache-Control "public, immutable";
}
# API路由
location /api/ {
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_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
# WebSocket支持
location /ws/ {
proxy_pass http://tomcat_cluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
# 管理后台
location /admin/ {
# 访问控制
allow 10.0.0.0/24;
deny all;
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
}
}
2. 容器化部署配置
dockerfile
# Dockerfile - Tomcat容器镜像
FROM tomcat:9.0-jdk17-corretto
# 时区设置
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装必要的工具
RUN apt-get update && apt-get install -y \
curl \
vim \
&& rm -rf /var/lib/apt/lists/*
# 创建应用目录
RUN mkdir -p /opt/apps /opt/static /var/log/tomcat
# 复制配置文件
COPY conf/server.xml /usr/local/tomcat/conf/server.xml
COPY conf/web.xml /usr/local/tomcat/conf/web.xml
COPY conf/context.xml /usr/local/tomcat/conf/context.xml
COPY conf/tomcat-users.xml /usr/local/tomcat/conf/tomcat-users.xml
# 复制应用WAR包
COPY apps/*.war /usr/local/tomcat/webapps/
# 复制启动脚本
COPY scripts/setenv.sh /usr/local/tomcat/bin/setenv.sh
RUN chmod +x /usr/local/tomcat/bin/setenv.sh
# 复制JVM参数配置
COPY conf/jvm.options /usr/local/tomcat/conf/jvm.options
# 设置健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# 暴露端口
EXPOSE 8080 8009
# 切换用户(安全考虑)
USER tomcat
# 启动命令
CMD ["catalina.sh", "run"]
yaml
# docker-compose.yml - 多容器编排
version: '3.8'
services:
tomcat-app:
image: ecommerce-tomcat:latest
container_name: ecommerce-tomcat
hostname: tomcat-node1
ports:
- "8080:8080"
- "8009:8009"
volumes:
- ./logs:/usr/local/tomcat/logs
- ./static:/opt/static
- ./conf/server.xml:/usr/local/tomcat/conf/server.xml
environment:
- JAVA_OPTS=-Xms2g -Xmx2g -Dapp.env=production
- DB_HOST=mysql-service
- REDIS_HOST=redis-service
networks:
- app-network
depends_on:
mysql-service:
condition: service_healthy
redis-service:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
nginx:
image: nginx:alpine
container_name: ecommerce-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
networks:
- app-network
depends_on:
- tomcat-app
networks:
app-network:
driver: bridge
Apache Tomcat的真正价值在于它为Java Web应用提供了一个成熟、稳定且性能优秀的运行环境。通过其精心设计的容器架构、灵活的配置选项和丰富的扩展能力,Tomcat能够满足从开发测试到生产环境的各种需求。特别是在微服务架构流行的今天,Tomcat作为独立的应用容器,依然是许多Spring Boot应用的默认选择。
然而,要充分发挥Tomcat的性能,需要深入理解其内部原理并进行恰当的优化配置。从线程池调优到内存管理,从集群配置到安全加固,每一个环节都需要精心设计。在实际生产环境中,Tomcat通常不是孤立运行的,而是与Nginx、Redis、数据库等组件协同工作,共同构建完整的技术栈。
看完这篇文章,你是否在生产环境中部署和优化过Tomcat?或者你在Tomcat性能调优方面有什么独特的经验?欢迎在评论区分享你的Tomcat实战心得,也欢迎提出关于Java Web应用部署和运维的任何技术问题,让我们一起探讨如何更好地构建稳定、高效的Web应用基础设施!
更多推荐




所有评论(0)