
Apache Tomcat诞生于1999年,最初由Sun Microsystems的工程师James Duncan Davidson开发。作为Jakarta项目的一部分,Tomcat最初是Apache Software Foundation和Sun公司合作开发的产物。其名称"Tomcat"源自开发者对暹罗猫的喜爱,同时也反映了其轻量级、敏捷的特性。
Tomcat的早期版本主要实现了Servlet 2.2和JSP 1.1规范。随着Java企业版技术的快速发展,Tomcat逐渐成为最流行的Java Web应用服务器之一。2005年,Tomcat 5.x系列发布,全面支持Servlet 2.4和JSP 2.0规范,标志着Tomcat进入成熟阶段。
Tomcat是一个开源的Java Servlet容器,实现了Java Servlet和JavaServer Pages (JSP)规范。虽然Tomcat不是一个完整的Java EE应用服务器(它不提供EJB、JMS等企业级特性),但其轻量级、高性能的特点使其成为部署Web应用的首选平台。
在微服务架构流行的今天,Tomcat因其资源占用少、启动速度快的特点,成为Spring Boot等框架内嵌容器的首选。据统计,超过60%的Java Web应用选择Tomcat作为运行环境。
每个版本都在性能、安全性和可管理性方面有显著改进。例如,Tomcat 8引入了非阻塞IO(NIO2)连接器,大幅提升了并发处理能力;Tomcat 9优化了HTTP/2支持;Tomcat 10则适应了Jakarta EE的命名空间变化。
Tomcat采用模块化、分层式的架构设计,核心设计原则包括:
Server是Tomcat的最顶层组件,代表整个Tomcat实例。一个JVM进程通常只包含一个Server实例,负责管理整个容器的生命周期。
// Server接口的核心方法
public interface Server extends Lifecycle {
public void addService(Service service);
public void removeService(Service service);
public Service findService(String name);
public void setPort(int port);
public int getPort();
public void setAddress(InetAddress address);
// ...
}Server通过监听特定端口(默认8005)接收shutdown命令,实现优雅关闭。这种设计确保了Tomcat实例可以安全地停止服务,而不会导致正在处理的请求中断。
Service是连接器(Connector)和容器(Container)的集合,一个Server可以包含多个Service,每个Service提供独立的服务能力。这种设计允许在同一Tomcat实例中部署多个独立的应用组。
<!-- server.xml中Service配置示例 -->
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<!-- 容器配置 -->
</Engine>
</Service>Connector是Tomcat的网络通信模块,负责处理客户端连接、协议解析和请求转发。Tomcat支持多种协议的Connector:
// Connector类的初始化过程
public class Connector extends LifecycleMBeanBase {
protected void initInternal() throws LifecycleException {
super.initInternal();
// 根据协议创建处理器
if (protocolHandler == null) {
if (getProtocol() != null) {
switch (getProtocol()) {
case "HTTP/1.1":
protocolHandler = new Http11NioProtocol();
break;
case "AJP/1.3":
protocolHandler = new AjpNioProtocol();
break;
// 其他协议处理
}
}
}
protocolHandler.setExecutor(getExecutor());
}
}AJP(Apache JServ Protocol)是专门为Tomcat与Web服务器(如Apache HTTPD)之间通信设计的二进制协议,比HTTP更高效。AJP Connector通常用于生产环境中Tomcat与前端Web服务器的集成。
<!-- 高性能NIO连接器配置示例 -->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="10000"
connectionTimeout="20000"
keepAliveTimeout="30000"
maxKeepAliveRequests="100"
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript"
enableLookups="false"
redirectPort="8443" />Container是Tomcat的请求处理容器,采用分层结构,包括四个层级:
Engine是最高级别的容器,代表整个Servlet引擎。一个Service只能包含一个Engine,但一个Engine可以包含多个Host。
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- 认证领域配置 -->
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- 主机配置 -->
</Host>
</Engine>Host代表一个虚拟主机,允许在同一Tomcat实例中部署多个域名的应用。每个Host有自己的webapps目录和应用部署机制。
Context是基本的Web应用容器,每个WAR包或Web应用目录对应一个Context。Context负责管理Servlet、Filter、Listener等Web组件。
<!-- context.xml配置示例 -->
<Context docBase="myapp" path="/myapp" reloadable="true">
<Resource name="jdbc/myDB" auth="Container"
type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="dbuser" password="dbpass"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb"/>
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" prefix="myapp_access." suffix=".log"
pattern="%h %l %u %t "%r" %s %b" />
</Context>Wrapper是容器层级的最底层,代表单个Servlet定义。它负责管理Servlet的生命周期和请求分发。
Tomcat采用统一的生命周期管理机制,所有组件都实现Lifecycle接口,确保组件启动、停止的有序性。
// Lifecycle接口定义
public interface Lifecycle {
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public void addLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
public LifecycleState getState();
public String getStateName();
}生命周期状态转换遵循严格的顺序:NEW → INITIALIZING → INITIALIZED → STARTING_PREP → STARTING → STARTED → STOPPING_PREP → STOPPING → STOPPED → DESTROYING → DESTROYED。
Tomcat实现了自定义的类加载器体系,解决了Web应用隔离和资源共享的问题:
这种分层类加载机制确保了:
// Tomcat类加载器体系
public class WebappClassLoader extends URLClassLoader {
// 重写loadClass方法实现加载规则
public Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 1. 检查本地已加载的类
Class<?> clazz = findLoadedClass0(name);
if (clazz != null) {
return clazz;
}
// 2. 使用系统类加载器避免重复加载
clazz = system.loadClass(name);
if (clazz != null) {
return clazz;
}
// 3. 检查安全包访问权限
if (securityManager != null) {
// 安全检查逻辑
}
// 4. 委托给父加载器(Common ClassLoader)
boolean delegateLoad = delegate || filter(name, true);
if (delegateLoad) {
try {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
return clazz;
}
} catch (ClassNotFoundException e) {
// 忽略,继续向下查找
}
}
// 5. 本地查找
clazz = findClass(name);
if (clazz != null) {
return clazz;
}
// 6. 最终委托给系统类加载器
if (!delegateLoad) {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
return clazz;
}
}
throw new ClassNotFoundException(name);
}
}Tomcat连接器的性能很大程度上取决于其采用的I/O模型和线程模型。现代Tomcat版本主要推荐使用NIO或NIO2模型。
BIO是传统的阻塞式I/O模型,每个连接需要一个专用线程处理。当连接数增多时,线程上下文切换开销巨大,不适合高并发场景。
// BIO工作线程伪代码
while (running) {
Socket socket = serverSocket.accept(); // 阻塞等待连接
Executor.execute(new Runnable() {
public void run() {
InputStream input = socket.getInputStream();
// 读取请求 -> 处理 -> 写入响应
// 整个过程线程被阻塞
}
});
}NIO基于Java的Selector机制,使用少量线程处理大量连接。通过事件驱动机制,只有在数据准备好时才进行读写操作。
// NIO工作流程
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
// 处理新连接
}
if (key.isReadable()) {
// 处理读事件
}
if (key.isWritable()) {
// 处理写事件
}
}
}NIO2引入了真正的异步I/O支持,通过回调机制处理I/O操作,进一步减少了线程阻塞和上下文切换。
// NIO2异步处理
AsynchronousServerSocketChannel serverChannel =
AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(port));
serverChannel.accept(null,
new CompletionHandler<AsynchronousSocketChannel, Void>() {
public void completed(AsynchronousSocketChannel channel, Void attachment) {
serverChannel.accept(null, this); // 接受下一个连接
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer, buffer,
new CompletionHandler<Integer, ByteBuffer>() {
public void completed(Integer result, ByteBuffer buffer) {
// 处理读取的数据
buffer.flip();
channel.write(buffer, null,
new CompletionHandler<Integer, Void>() {
public void completed(Integer result, Void attachment) {
// 写入完成
}
public void failed(Throwable exc, Void attachment) {
// 处理错误
}
});
}
public void failed(Throwable exc, ByteBuffer buffer) {
// 处理错误
}
});
}
public void failed(Throwable exc, Void attachment) {
// 处理错误
}
});ProtocolHandler是连接器的核心组件,负责协议解析和请求处理。Tomcat采用了模块化的ProtocolHandler设计:
// ProtocolHandler处理流程
public abstract class AbstractProtocol<S> implements ProtocolHandler {
protected abstract class AbstractEndpoint<S> {
public abstract void bind() throws Exception;
public abstract void start() throws Exception;
public abstract void stop() throws Exception;
protected abstract boolean setSocketOptions(S socket);
protected abstract void closeSocket(S socket);
}
protected static class ConnectionHandler<S>
implements AbstractEndpoint.Handler<S> {
public void process(S socket) {
Processor processor = getProcessor();
if (processor == null) {
processor = createProcessor();
}
try {
// 协议解析
processor.process(socket);
// 通过Adapter调用容器
getAdapter().service(request, response);
// 回收处理器
recycleProcessor(processor);
} catch (Exception e) {
// 错误处理
}
}
}
}HTTP ProtocolHandler负责解析HTTP请求,包括:
// HTTP请求解析示例
public class Http11Processor extends AbstractProcessor {
protected void parseRequestLine() throws IOException {
// 读取请求行
String line = readLine();
// 解析方法
int spaceIndex = line.indexOf(' ');
if (spaceIndex == -1) {
throw new IllegalArgumentException("Invalid request line");
}
String method = line.substring(0, spaceIndex);
// 解析URI
int nextSpaceIndex = line.indexOf(' ', spaceIndex + 1);
if (nextSpaceIndex == -1) {
throw new IllegalArgumentException("Invalid request line");
}
String uri = line.substring(spaceIndex + 1, nextSpaceIndex);
// 解析协议版本
String protocol = line.substring(nextSpaceIndex + 1);
request.method().setString(method);
request.requestURI().setString(uri);
request.protocol().setString(protocol);
}
}Tomcat使用Executor组件管理线程池,合理配置线程池参数对性能至关重要:
<!-- server.xml中线程池配置 -->
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="500"
minSpareThreads="50"
maxIdleTime="60000"
maxQueueSize="Integer.MAX_VALUE"
prestartminSpareThreads="false"/>参数说明:
<Connector port="8080" protocol="HTTP/1.1"
maxConnections="10000" <!-- 最大连接数 -->
acceptCount="100" <!-- 等待队列长度 -->
connectionTimeout="20000" <!-- 连接超时时间 -->
keepAliveTimeout="30000" <!-- 保持连接超时 -->
maxKeepAliveRequests="100" <!-- 每个连接最大请求数 -->
tcpNoDelay="true" <!-- 禁用Nagle算法 -->
socketBuffer="-1" <!-- Socket缓冲区大小 -->
/><Connector port="8080" protocol="HTTP/1.1"
compression="on" <!-- 启用压缩 -->
compressionMinSize="2048" <!-- 最小压缩大小 -->
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript"
useSendfile="true" <!-- 使用零拷贝传输 -->
sendfileSize="256" <!-- 零拷贝阈值 -->
/>Tomcat容器采用管道-阀门(Pipeline-Valve)模式处理请求,这种设计模式类似于Servlet过滤器链,但更底层和灵活。
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void removeValve(Valve valve);
public Container getContainer();
public void setContainer(Container container);
}
public interface Valve {
public String getInfo();
public Valve getNext();
public void setNext(Valve valve);
public void invoke(Request request, Response response)
throws IOException, ServletException;
public void backgroundProcess();
public boolean isAsyncSupported();
}Tomcat提供了多个标准Valve实现:
<!-- Valve配置示例 -->
<Engine name="Catalina" defaultHost="localhost">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="192\.168\.\d+\.\d+" deny=""/>
</Engine>Tomcat提供了强大的会话管理功能,支持多种会话存储和持久化方式。
// 会话管理器接口
public interface Manager {
public Session createSession(String sessionId);
public Session findSession(String id);
public void add(Session session);
public void remove(Session session);
public void processExpires();
public void load();
public void unload();
public int getActiveSessions();
}<!-- 会话管理器配置 -->
<Manager className="org.apache.catalina.session.PersistentManager"
saveOnRestart="true"
maxActiveSessions="1000"
minIdleSwap="0"
maxIdleSwap="60"
maxIdleBackup="0">
<Store className="org.apache.catalina.session.FileStore"
directory="./sessions"/>
</Manager>在集群环境中,Tomcat支持多种会话复制策略:
<!-- 集群会话配置 -->
<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.MessageDispatchInterceptor"/>
</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"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>Tomcat提供了完整的安全管理体系,包括认证、授权和数据保护。
Realm是Tomcat的安全数据库接口,支持多种后端存储:
<!-- Realm配置示例 -->
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="org.postgresql.Driver"
connectionURL="jdbc:postgresql://localhost/mydb"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name"
digest="SHA-256" />通过web.xml配置安全约束:
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure Area</web-resource-name>
<url-pattern>/secure/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Form-Based Authentication Area</realm-name>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>Tomcat集成了Jasper JSP引擎,负责JSP页面的编译和执行。
// JSP编译流程
public class JspCompilationContext {
public void compile() throws JasperException {
// 1. 生成Java源文件
generateJavaSource();
// 2. 编译Java文件
if (isCompile()) {
createCompiler();
compiler.compile();
// 3. 更新JSP依赖跟踪
updateDependencies();
}
}
}<!-- Jasper配置 -->
<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>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>development</param-name>
<param-value>false</param-value> <!-- 生产环境关闭开发模式 -->
</init-param>
<init-param>
<param-name>checkInterval</param-name>
<param-value>300</param-value> <!-- 检查间隔(秒) -->
</init-param>
<init-param>
<param-name>modificationTestInterval</param-name>
<param-value>60</param-value> <!-- 修改测试间隔 -->
</init-param>
<init-param>
<param-name>compiler</param-name>
<param-value>modern</param-value> <!-- 使用现代编译器 -->
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>Tomcat支持Servlet 3.0+的异步处理特性,允许长时间操作在不阻塞请求线程的情况下完成。
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(30000); // 设置超时时间
// 提交异步任务
executorService.submit(new AsyncTask(asyncContext));
}
}
class AsyncTask implements Runnable {
private AsyncContext asyncContext;
public AsyncTask(AsyncContext asyncContext) {
this.asyncContext = asyncContext;
}
public void run() {
try {
// 执行长时间操作
Thread.sleep(5000);
// 完成异步处理
ServletResponse response = asyncContext.getResponse();
response.getWriter().write("Async response");
asyncContext.complete();
} catch (Exception e) {
// 错误处理
}
}
}<!-- 异步处理相关配置 -->
<Connector port="8080" protocol="HTTP/1.1"
asyncTimeout="30000" <!-- 异步超时时间 -->
maxThreads="200" <!-- 减少工作线程数 -->
/>Tomcat提供了完整的WebSocket实现,支持实时双向通信。
@ServerEndpoint("/websocket/{room}")
public class ChatEndpoint {
@OnOpen
public void onOpen(Session session, @PathParam("room") String room) {
session.getUserProperties().put("room", room);
}
@OnMessage
public void onMessage(String message, Session session) {
String room = (String) session.getUserProperties().get("room");
// 广播消息到同一房间
for (Session s : session.getOpenSessions()) {
if (s.isOpen() && room.equals(s.getUserProperties().get("room"))) {
s.getAsyncRemote().sendText(message);
}
}
}
@OnClose
public void onClose(Session session) {
// 清理资源
}
@OnError
public void onError(Throwable t) {
// 错误处理
}
}<!-- WebSocket配置 -->
<Executor name="webSocketExecutor"
maxThreads="100"
maxQueueSize="1000"/>
<Connector port="8080" protocol="HTTP/1.1"
maxWebSocketConnections="10000" <!-- 最大WebSocket连接数 -->
/>在生产环境中,Tomcat通常与前端Web服务器(如Nginx、Apache HTTPD)集成。
# Nginx配置示例
upstream tomcat_cluster {
server 192.168.1.10:8080 weight=1;
server 192.168.1.11:8080 weight=1;
server 192.168.1.12:8080 weight=1;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
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_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;
}
# 静态文件由Nginx直接处理
location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf)$ {
root /opt/static;
expires 7d;
access_log off;
}
}# Apache配置
LoadModule jk_module modules/mod_jk.so
JkWorkersFile conf/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel info
JkMount /* loadbalancer
# workers.properties
worker.list=loadbalancer
worker.worker1.port=8009
worker.worker1.host=192.168.1.10
worker.worker1.type=ajp13
worker.worker1.lbfactor=1
worker.worker2.port=8009
worker.worker2.host=192.168.1.11
worker.worker2.type=ajp13
worker.worker2.lbfactor=1
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=worker1,worker2Tomcat提供了多种监控和管理功能,帮助运维人员了解系统状态。
通过JMX可以监控Tomcat的各项指标:
// JMX监控示例
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName threadPoolName = new ObjectName(
"Catalina:type=ThreadPool,name=\"http-nio-8080\"");
ThreadPoolMXBean threadPoolProxy = JMX.newMXBeanProxy(
mBeanServer, threadPoolName, ThreadPoolMXBean.class);
System.out.println("当前活跃线程数: " + threadPoolProxy.getActiveCount());
System.out.println("最大线程数: " + threadPoolProxy.getMaximumPoolSize());<!-- 启用JMX监控 -->
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
rmiRegistryPortPlatform="10099"
rmiServerPortPlatform="10098" />
<!-- 状态管理页面 -->
<Valve className="org.apache.catalina.valves.StatusValve" />
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1|192\.168\.\d+\.\d+" />Tomcat性能调优需要系统化的方法,包括监控、分析、调优和验证四个阶段。
# Tomcat启动参数
export JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize=1g -XX:MaxNewSize=1g \
-XX:PermSize=256m -XX:MaxPermSize=256m -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45 \
-XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
-Xloggc:/opt/tomcat/logs/gc.log -XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M"参数说明:
Tomcat提供了内存泄漏检测功能:
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />通过JMX监控线程池状态:
// 线程池监控代码
public class ThreadPoolMonitor {
public void monitor() throws Exception {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> objectNames = mBeanServer.queryNames(
new ObjectName("Catalina:type=ThreadPool,*"), null);
for (ObjectName objectName : objectNames) {
ThreadPoolMXBean threadPool = JMX.newMXBeanProxy(
mBeanServer, objectName, ThreadPoolMXBean.class);
System.out.println("连接器: " + objectName.getKeyProperty("name"));
System.out.println("当前线程数: " + threadPool.getCurrentThreadCount());
System.out.println("繁忙线程数: " + threadPool.getActiveCount());
System.out.println("最大线程数: " + threadPool.getMaximumPoolSize());
System.out.println("任务队列大小: " + threadPool.getQueueSize());
}
}
}使用jstack生成线程转储:
jstack -l <pid> > thread_dump.txt分析线程状态:
# 查看TCP连接状态
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
# 调整TCP参数
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 3000 > /proc/sys/net/ipv4/tcp_fin_timeout# 删除不必要的默认应用
rm -rf $CATALINA_HOME/webapps/docs
rm -rf $CATALINA_HOME/webapps/examples
rm -rf $CATALINA_HOME/webapps/host-manager
rm -rf $CATALINA_HOME/webapps/manager
rm -rf $CATALINA_HOME/webapps/ROOT<!-- web.xml中配置安全HTTP头 -->
<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>hstsMaxAgeSeconds</param-name>
<param-value>31536000</param-value>
</init-param>
<init-param>
<param-name>antiClickJackingEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>antiClickJackingOption</param-name>
<param-value>SAMEORIGIN</param-value>
</init-param>
<init-param>
<param-name>blockContentTypeSniffingEnabled</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping><!-- conf/web.xml中配置 -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Restricted methods</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>tomcat/
├── bin/ # 启动脚本
├── conf/ # 配置文件
├── logs/ # 日志文件
├── webapps/ # 应用部署目录
│ ├── app1/ # 应用1
│ ├── app2/ # 应用2
│ └── ROOT/ # 根应用(可选)
├── work/ # 工作目录
├── temp/ # 临时目录
└── lib/ # 共享库目录<!-- context.xml中配置应用隔离 -->
<Context>
<!-- 防止应用访问其他应用的类 -->
<Loader delegate="true"/>
<!-- 限制文件系统访问 -->
<Resources allowLinking="false"/>
<!-- 禁用会话持久化 -->
<Manager pathname=""/>
<!-- 限制JSP编译权限 -->
<JspServlet development="false"
modificationTestInterval="300"/>
</Context><!-- server.xml中配置访问日志 -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="%{X-Forwarded-For}i %l %u %t "%r" %s %b %D %{User-Agent}i"
rotatable="true"
conditionUnless="doNotLog"
renameOnRotate="true"
fileDateFormat="yyyy-MM-dd.HH"/>// 自定义审计过滤器
public class AuditFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String username = httpRequest.getRemoteUser();
String uri = httpRequest.getRequestURI();
String method = httpRequest.getMethod();
String params = httpRequest.getQueryString();
// 记录审计日志
auditLogger.info("用户: {}, 操作: {}, 资源: {}, 参数: {}",
username, method, uri, params);
chain.doFilter(request, response);
}
}FROM openjdk:8-jre-alpine
# 安装必要的工具
RUN apk add --no-cache bash curl
# 创建Tomcat用户和组
RUN addgroup -S tomcat && adduser -S tomcat -G tomcat
# 设置Tomcat版本
ENV TOMCAT_MAJOR=9 \
TOMCAT_VERSION=9.0.54 \
CATALINA_HOME=/opt/tomcat \
CATALINA_BASE=/opt/tomcat
# 下载并安装Tomcat
RUN mkdir -p ${CATALINA_HOME} \
&& curl -O https://archive.apache.org/dist/tomcat/tomcat-${TOMCAT_MAJOR}/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz \
&& tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz -C ${CATALINA_HOME} --strip-components=1 \
&& rm apache-tomcat-${TOMCAT_VERSION}.tar.gz
# 清理默认应用
RUN rm -rf ${CATALINA_HOME}/webapps/*
# 复制配置文件
COPY conf/ ${CATALINA_HOME}/conf/
# 复制应用
COPY webapps/ ${CATALINA_HOME}/webapps/
# 设置权限
RUN chown -R tomcat:tomcat ${CATALINA_HOME} \
&& chmod -R u+rX ${CATALINA_HOME} \
&& chmod -R g+rX ${CATALINA_HOME}
# 切换用户
USER tomcat
# 暴露端口
EXPOSE 8080
# 启动命令
CMD ["catalina.sh", "run"]apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
labels:
app: tomcat
spec:
replicas: 3
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: my-tomcat:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /manager/text/serverinfo
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /manager/text/serverinfo
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
env:
- name: JAVA_OPTS
value: "-Xms1g -Xmx1g -XX:+UseG1GC"
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
selector:
app: tomcat
ports:
- port: 80
targetPort: 8080
type: LoadBalancerapiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: tomcat-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: tomcat-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80livenessProbe:
httpGet:
path: /manager/text/serverinfo
port: 8080
httpHeaders:
- name: Authorization
value: Basic dG9tY2F0OnNlY3JldA== # base64编码的凭证
initialDelaySeconds: 120
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /manager/text/serverinfo
port: 8080
httpHeaders:
- name: Authorization
value: Basic dG9tY2F0OnNlY3JldA==
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-with-istio
annotations:
sidecar.istio.io/inject: "true"
spec:
template:
metadata:
annotations:
sidecar.istio.io/rewriteAppHTTPProbers: "true"
spec:
containers:
- name: tomcat
image: my-tomcat:latest
# 其他配置...<!-- context.xml中配置追踪 -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto"
protocolHeaderHttpsValue="https"/>
<!-- 在应用中添加追踪头 -->
<filter>
<filter-name>tracingFilter</filter-name>
<filter-class>com.example.TracingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>tracingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>// Bootstrap类的main方法
public static void main(String args[]) {
// 创建Bootstrap实例
Bootstrap bootstrap = new Bootstrap();
try {
// 初始化类加载器
initClassLoaders();
// 设置当前线程类加载器
Thread.currentThread().setContextClassLoader(catalinaLoader);
// 加载Catalina类
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// 调用start方法
String methodName = "start";
Method method = startupInstance.getClass().getMethod(methodName);
method.invoke(startupInstance);
} catch (Exception e) {
// 异常处理
}
}// Catalina类的启动方法
public void start() {
// 1. 加载服务器配置
load();
// 2. 创建并启动Server
getServer().init();
getServer().start();
// 3. 注册关闭钩子
if (useShutdownHook) {
Thread shutdownHook = new CatalinaShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
// 4. 等待停止命令
if (await) {
await();
stop();
}
}// AbstractEndpoint处理连接
protected boolean processSocket(SocketWrapperBase<S> socketWrapper) {
try {
// 获取处理器
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
// 提交处理任务
Executor executor = getExecutor();
if (executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (Exception e) {
// 异常处理
}
return true;
}// StandardWrapperValve invoke方法
public final void invoke(Request request, Response response) {
// 1. 分配Servlet实例
Servlet servlet = wrapper.allocate();
// 2. 创建ApplicationFilterChain
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 3. 调用过滤器链和Servlet
try {
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request.getRequest(), response.getResponse());
}
} finally {
// 4. 释放资源
if (filterChain != null) {
filterChain.release();
}
wrapper.deallocate(servlet);
}
}// WebappClassLoader加载类逻辑
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 1. 检查本地已加载类
Class<?> clazz = findLoadedClass0(name);
if (clazz != null) {
return clazz;
}
// 2. 检查系统类
clazz = system.loadClass(name);
if (clazz != null) {
return clazz;
}
// 3. 安全检查
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
securityManager.checkPackageAccess(name.substring(0, i));
}
}
// 4. 委托加载
boolean delegateLoad = delegate || filter(name, true);
if (delegateLoad) {
try {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
return clazz;
}
} catch (ClassNotFoundException e) {
// 忽略,继续向下查找
}
}
// 5. 本地查找
clazz = findClass(name);
if (clazz != null) {
return clazz;
}
// 6. 最终委托
if (!delegateLoad) {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
return clazz;
}
}
throw new ClassNotFoundException(name);
}Tomcat作为最流行的Java Web容器,其成功源于其稳定可靠的架构、优秀的性能和丰富的功能特性。通过深入了解Tomcat的内部机制,开发者和运维人员可以:
随着技术的不断发展,Tomcat也在持续演进,拥抱新的技术和架构范式。无论是传统的企业应用还是现代的云原生应用,Tomcat都能提供可靠的基础设施支持。
作为技术人员,我们应该:
通过不断学习和实践,我们可以充分发挥Tomcat的潜力,构建高性能、高可用的Java Web应用系统。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。