Tomcat是Apache软件基金会旗下的开源Web容器,实现了Jakarta Servlet、Jakarta Server Pages和WebSocket等规范。它在Java Web体系中扮演着连接HTTP协议与后端业务逻辑的桥梁角色:当浏览器发起请求时,Tomcat负责建立TCP连接、解析HTTP报文、定位对应的Web应用和Servlet,再把Servlet生成的响应返回给客户端。除此之外,Tomcat还承担会话管理、类加载隔离、静态资源访问和应用生命周期控制等任务。理解这些作用,是排查部署问题、进行性能调优和安全加固的基础。

一、Tomcat服务器的主要作用
Tomcat最核心的作用是充当Servlet容器。Servlet本身只是一组Java接口和类,它需要一个运行环境来实例化、初始化、调用和销毁。Tomcat会读取web.xml或@WebServlet注解,把不同的URL路径映射到对应的Servlet实现类上。每次请求到达时,Tomcat从线程池中取出一个工作线程,创建HttpServletRequest和HttpServletResponse对象,然后调用Servlet的doGet或doPost方法。与只能返回静态文件的HTTP服务器不同,Servlet可以查询数据库、执行复杂业务逻辑并动态生成HTML、JSON等响应内容,这是Tomcat在Java Web项目中不可替代的原因。
除了Servlet规范,Tomcat还支持JSP和WebSocket。JSP页面在第一次访问时会被翻译成Java源文件,再编译为Servlet类,后续请求直接复用这个类。WebSocket则让服务端和客户端可以维持长连接,适合聊天、消息推送等场景。需要注意,Tomcat 10之后底层包名从javax.servlet迁移到了jakarta.servlet,如果项目仍然使用旧版依赖,部署时会出现ClassNotFoundException或NoClassDefFoundError。这也是升级Tomcat时经常遇到的兼容性问题。
Tomcat自身也具备静态资源服务能力,内置连接器可以处理HTTP/1.1和HTTP/2请求。但在高并发场景下,通常会让Nginx或Apache HTTP Server处理静态文件、SSL终止和负载均衡,再把动态请求反向代理给Tomcat。这样可以发挥Nginx在静态资源上的高效率,也能减少Tomcat线程被长时间占用。Tomcat同时维护会话状态,通过JSESSIONID和Cookie识别同一客户端,并支持Session持久化到文件或数据库,避免重启后会话全部丢失。
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("Hello Tomcat");
}
}
二、Tomcat核心组件与请求处理链路
从配置文件角度看,Tomcat的组件结构是分层级的。最顶层是Server,代表整个Tomcat实例;一个Server可以包含多个Service,每个Service由若干个Connector和一个Engine组成。Connector负责接收客户端连接、解析协议,Engine负责处理请求。一个Engine下可以配置多个Host虚拟主机,每个Host下管理多个Context,也就是Web应用,Context内部又通过Wrapper包装具体的Servlet。理解这个层级关系,有助于快速定位配置文件和排错。
下面的server.xml片段展示了一个最简配置。Tomcat启动时会解析它并构建对应的组件树。实际生产环境中,不建议把所有配置集中在server.xml中,可以拆分为context.xml、web.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">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="/app" docBase="app" />
</Host>
</Engine>
</Service>
</Server>
一次请求的大致流程是:Connector接收到HTTP请求后解析出请求行、请求头和请求体,封装为Request对象;然后交给Engine,Engine根据请求头中的Host字段选择对应的Host;Host再根据URL前缀匹配到某个Context;Context执行过滤链,最后找到对应的Wrapper调用Servlet的service方法。由于Servlet默认在多线程环境下运行,实例变量并不是线程安全的,因此编写Servlet时尽量避免在类中保存可变共享状态。
三、Tomcat进阶配置与性能优化
Tomcat性能调优一般从连接器线程池入手。maxThreads决定同时处理请求的最大线程数,minSpareThreads是空闲线程保底数量,acceptCount表示当所有线程都在忙碌时等待队列的最大长度。maxConnections控制长连接总量。这些参数并非越大越好:线程数过多会增加上下文切换和内存消耗,过少则会让请求在队列中等待。建议先基于压测结果逐步调整,而不是直接把maxThreads设成几千。
JVM内存配置同样关键。对于大多数Web应用,建议在CATALINA_OPTS中显式指定堆内存和元空间上限,避免默认值过小或过大。Linux环境下可以这样设置:
export CATALINA_OPTS="-Xms2g -Xmx4g -XX:MaxMetaspaceSize=512m -XX:+UseG1GC"
Windows环境下则使用set命令,路径和参数中的反斜杠需要保持原样。例如进入安装目录C:\apache-tomcat\bin后执行catalina.bat run,可以在控制台观察启动日志。堆内存不足时会出现OutOfMemoryError: Java heap space,而频繁热部署或不停机更新应用可能导致OutOfMemoryError: Metaspace,两者要区分处理。
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="400"
minSpareThreads="50"
acceptCount="200"
maxConnections="10000"
connectionTimeout="5000"
enableLookups="false"
compression="on"
compressionMinSize="2048"
redirectPort="8443" />
除线程池和JVM外,还可以开启响应压缩、设置静态资源缓存时间、使用NIO或NIO2协议、配置JNDI数据源让连接池由容器管理。对于需要横向扩展的场景,可以部署多个Tomcat实例,通过Nginx或Apache做负载均衡,并使用Redis或数据库实现Session共享。优化前需要明确当前瓶颈是CPU、内存、线程池还是数据库,否则只调整Tomcat参数往往收效有限。
四、常见问题与注意事项
生产环境中最常见的问题是内存溢出。堆内存不足时,Tomcat日志会出现java.lang.OutOfMemoryError,此时需要分析堆转储文件,检查是否存在大对象或内存泄漏。而元空间溢出通常与类加载器泄漏有关。Tomcat每个Web应用使用独立的类加载器,如果应用反复reload或重新部署,旧类加载器如果仍被线程或静态变量引用,就无法被垃圾回收,最终导致Metaspace被耗尽。解决思路是避免在生产环境频繁热部署,必要时重启应用服务器,并检查代码中是否有线程池、定时任务或JDBC驱动注册导致类加载器无法释放。
端口占用和中文乱码也很常见。8080端口被其他程序占用时,可以修改server.xml中<Connector>的port属性,也可以使用netstat -ano找到占用进程。GET请求中文参数乱码通常是因为连接器没有指定URIEncoding="UTF-8";POST请求乱码则要在过滤器中设置request.setCharacterEncoding("UTF-8")。安全方面,应该删除webapps目录下自带的examples、docs、host-manager等应用,禁用目录列表,限制manager访问IP,并修改默认的shutdown端口和指令。
如果启用HTTPS,需要先生成密钥库,再配置连接器。不要使用过时的SSLv3或TLSv1.0协议,建议至少使用TLSv1.2。配置中涉及证书路径时,Windows环境下要保留反斜杠,例如keystoreFile="conf\keystore.jks"。以下是一个HTTPS连接器示例:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="200"
SSLEnabled="true"
scheme="https"
secure="true"
keystoreFile="conf\keystore.jks"
keystorePass="changeit"
clientAuth="false"
sslProtocol="TLS" />
还需要关注日志体积。Tomcat默认的catalina.out、localhost.log等文件如果不做切割,可能占满磁盘。可以通过日志框架或系统级logrotate控制大小。总的来说,Tomcat本身的默认配置适合开发和小流量场景,上线前应结合业务量、部署方式和监控数据做针对性的调整。

