Tomcat底层原理与实战全解析

简介: 本文全面解析Tomcat的底层原理与实战应用。作为轻量级JavaEE容器,Tomcat由HTTP服务器和Servlet容器组成,采用分层架构(Server→Service→Engine→Host→Context)。文章详细讲解请求处理流程、安装配置优化、Web应用部署方式,并提供SpringBoot+Tomcat的完整实战案例。针对生产环境,重点介绍性能优化策略(JVM调优、Connector配置)、故障排查工具(jstack、jmap)以及高可用方案(Nginx负载均衡+Redis会话共享)。

引言:为什么Tomcat是Java开发者的必备技能?

在Java后端开发领域,Tomcat绝对是绕不开的核心组件。无论是小型创业公司的单体应用,还是大型企业的分布式架构,Tomcat都以其轻量、稳定、可扩展的特性,成为Java Web应用的首选容器。作为Java资深技术专家,我将从底层原理、核心组件、实战配置、性能优化到故障排查,用最通俗的语言讲透Tomcat,结合可直接运行的实战案例,让你既能夯实基础,又能解决实际开发中的各类问题。

一、Tomcat核心认知:什么是Tomcat?它能做什么?

1.1 Tomcat的本质与定位

Tomcat是Apache软件基金会旗下的开源Java Servlet容器,同时支持JSP、EL表达式等Java Web技术规范,本质上是一个基于Java的HTTP服务器 + Servlet容器的组合体。它的核心作用有两个:一是作为HTTP服务器,接收客户端的HTTP请求并返回响应;二是作为Servlet容器,管理Servlet的生命周期(加载、初始化、运行、销毁),并提供Servlet运行所需的环境(如Request、Response对象的创建与传递)。

这里需要明确:Tomcat并非全功能的Web服务器(如Nginx、Apache HTTP Server),它的HTTP服务器功能主要用于开发和中小型部署场景;在大型生产环境中,通常会将Tomcat与Nginx配合使用——Nginx负责处理静态资源、负载均衡、SSL终结,Tomcat专注于处理动态请求(Java Web应用)。

1.2 Tomcat与Java EE规范的关系

Tomcat严格遵循Java EE(现Jakarta EE)规范中的Servlet、JSP、WebSocket等核心规范,但其支持的规范子集有限(不包含EJB、JMS等重型规范),因此被称为“轻量级Java EE容器”。最新稳定版本Tomcat 10.1已完全支持Jakarta EE 10规范,将包名从javax.servlet迁移为jakarta.servlet,这是后续版本的重要变更点,开发中需特别注意。

1.3 Tomcat的核心优势
  • 开源免费:基于Apache许可证,无商业授权成本;
  • 轻量灵活:安装包仅几十MB,配置简单,可根据需求裁剪组件;
  • 稳定可靠:经过多年工业级验证,支持高并发场景下的稳定运行;
  • 生态完善:与Spring、Spring Boot、MyBatis等主流Java框架无缝集成;
  • 可扩展性强:支持自定义Valve、Connector、Engine等组件,满足个性化需求。

二、Tomcat架构深度解析:从顶层结构到底层组件

2.1 Tomcat整体架构图


2.2 核心组件层级关系(从外到内)

Tomcat的架构采用分层设计,各组件职责清晰,层级关系为:Server → Service → Engine → Host → Context → Servlet容器,每个层级都包含特定的核心组件,共同完成请求的接收与处理。

2.2.1 Server:Tomcat的顶层容器

Server是Tomcat的最顶层组件,代表一个完整的Tomcat实例,一个Tomcat进程对应一个Server。其核心职责是管理Service组件(一个Server可包含多个Service,实现多端口监听),并提供Tomcat的启动、停止等生命周期管理能力。

Server的配置位于conf/server.xml<Server>标签中,默认配置如下:

<Server port="8005" shutdown="SHUTDOWN">
 <!-- 监听8005端口,接收SHUTDOWN命令关闭Tomcat -->
 <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
 <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
 <Service name="Catalina">
   <!-- 后续组件配置 -->
 </Service>
</Server>

2.2.2 Service:连接Connector与Engine的桥梁

Service组件的核心作用是“串联”Connector和Engine:将Connector接收的请求传递给Engine处理,同时管理这两个组件的生命周期(启动/停止同步)。一个Service包含一个Engine和多个Connector(不同Connector监听不同端口,支持不同协议)。

默认配置中,Service名称为“Catalina”(Tomcat的默认引擎名称),后续所有组件都隶属于该Service。

2.2.3 Connector:请求接收与协议解析组件

Connector(连接器)是Tomcat与客户端通信的入口,负责监听指定端口、接收客户端请求、解析请求协议(HTTP/1.1、AJP、HTTP/2等),并将解析后的请求封装为Tomcat内部的Request对象,传递给Engine处理;同时将Engine返回的Response对象转换为客户端可识别的协议格式,返回给客户端。

Tomcat的核心Connector实现有3种:

  1. HTTP Connector:处理HTTP/HTTPS请求,默认监听8080端口(HTTP)、8443端口(HTTPS);
  2. AJP Connector:处理AJP协议请求,默认监听8009端口,用于与Nginx、Apache HTTP Server等反向代理服务器通信(高效传递请求,减少协议转换开销);
  3. HTTP/2 Connector:支持HTTP/2协议,提供更高的并发性能和更低的延迟。

HTTP Connector核心配置示例conf/server.xml):

<Connector port="8080" protocol="HTTP/1.1"
          connectionTimeout="20000"
          redirectPort="8443"
          maxThreads="1000"
          minSpareThreads="100"
          acceptCount="1000"
          enableLookups="false"
          URIEncoding="UTF-8"/>

配置参数说明:

  • port:监听端口,默认8080;
  • protocol:协议类型,推荐使用org.apache.coyote.http11.Http11Nio2Protocol(NIO2模型,非阻塞IO,支持高并发);
  • connectionTimeout:连接超时时间(毫秒),默认20000;
  • redirectPort:HTTP请求重定向到HTTPS的端口(默认8443);
  • maxThreads:最大线程数(处理请求的核心线程池大小),默认200;
  • minSpareThreads:最小空闲线程数,确保有足够线程处理突发请求;
  • acceptCount:请求队列大小,当所有线程都繁忙时,最多可排队的请求数;
  • enableLookups:是否反向解析客户端IP对应的主机名(关闭可提升性能);
  • URIEncoding:URI编码格式,必须设置为UTF-8,避免中文参数乱码。
2.2.4 Engine:请求处理的核心引擎

Engine是Service的核心组件,也称为“引擎”,负责接收所有Connector传递的请求,并根据请求的主机名(Host)将请求分发到对应的Host组件处理。一个Engine可以包含多个Host(虚拟主机),实现多域名部署(如www.jam.com、blog.jam.com共用一个Tomcat实例)。

Engine的默认配置:

<Engine name="Catalina" defaultHost="localhost">
 <!-- 虚拟主机配置 -->
 <Host name="localhost"  appBase="webapps"
       unpackWARs="true" autoDeploy="true">

   <!-- Context配置 -->
 </Host>
</Engine>

参数说明:

  • name:引擎名称,默认“Catalina”;
  • defaultHost:默认虚拟主机名称,当请求的主机名无法匹配任何Host时,由该Host处理(默认localhost)。
2.2.5 Host:虚拟主机组件

Host代表一个虚拟主机,对应一个域名(如localhost、www.jam.com),负责管理多个Context(Web应用)。其核心职责是根据请求的URI路径,将请求分发到对应的Context组件(即具体的Web应用)。

Host的核心配置:

<Host name="localhost"  appBase="webapps"
     unpackWARs="true" autoDeploy="true">

 <!-- 别名配置:一个Host可对应多个域名 -->
 <Alias>www.jam.com</Alias>
 <!-- 访问日志配置 -->
 <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 &quot;%{Referer}i&quot; &quot;%{User-Agent}i&quot;"/>

</Host>

参数说明:

  • name:虚拟主机名称(必须与域名一致);
  • appBase:Web应用的部署目录(默认webapps,可改为自定义路径);
  • unpackWARs:是否自动解压WAR包(true则解压为文件夹,false直接运行WAR包);
  • autoDeploy:是否自动部署(监测appBase目录,新增/修改Web应用时自动加载,开发环境推荐开启,生产环境建议关闭);
  • Alias:虚拟主机别名,支持多个域名映射到同一个Host。
2.2.6 Context:Web应用上下文

Context代表一个具体的Web应用(如Spring Boot应用、传统JSP应用),是Tomcat中最小的部署单元。每个Context对应一个Web应用的目录结构,包含Servlet、JSP、静态资源、配置文件等。Context的核心职责是管理Web应用的生命周期,以及Servlet、Filter、Listener等组件的初始化与运行。

Context的配置方式有3种:

  1. 自动部署:将Web应用的WAR包或文件夹放入Host的appBase目录(如webapps),Tomcat自动创建Context;
  2. 配置文件方式:在conf/Catalina/localhost目录下创建xxx.xml文件(xxx为Context路径),配置Context信息;
  3. 直接在server.xml的Host标签内配置Context(不推荐,修改需重启Tomcat)。

推荐配置方式示例conf/Catalina/localhost/demo.xml):

<Context path="/demo" docBase="D:/webapps/demo" reloadable="false">
 <!-- 资源链接配置(如数据库连接池) -->
 <Resource name="jdbc/demoDB"
           auth="Container"
           type="javax.sql.DataSource"
           driverClassName="com.mysql.cj.jdbc.Driver"
           url="jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"
           username="root"
           password="root"
           maxTotal="100"
           maxIdle="20"
           minIdle="5"
           maxWaitMillis="10000"/>

</Context>

参数说明:

  • path:Web应用的访问路径(如/demo,则访问地址为http://localhost:8080/demo);
  • docBase:Web应用的实际物理路径(可绝对路径或相对路径);
  • reloadable:是否自动重载(监测classes和lib目录,文件变化时自动重启应用,开发环境true,生产环境false);
  • Resource:配置JNDI资源(如数据库连接池),供Web应用通过JNDI lookup获取。
2.2.7 Servlet容器:Web组件的运行环境

Servlet容器是Context的核心子组件,也称为“Servlet引擎”,负责管理Servlet、Filter、Listener的生命周期,提供Web应用运行所需的核心服务(如Request/Response对象创建、会话管理、安全控制等)。

Servlet容器的核心工作流程:

  1. 加载Web应用的web.xml配置文件(或注解配置),初始化Filter、Listener;
  2. 接收Context传递的请求,根据请求URI匹配对应的Servlet(通过映射规则);
  3. 若Servlet未初始化,则调用其init()方法初始化;
  4. 调用Servlet的service()方法处理请求(根据HTTP方法分发到doGet()/doPost()等方法);
  5. 处理完成后,将Response对象返回给Context,最终传递给客户端;
  6. 当Web应用停止时,调用Servlet的destroy()方法销毁实例。

三、Tomcat请求处理全流程:从客户端请求到响应返回

3.1 完整请求处理流程图

image.png

3.2 分步拆解核心流程

以“用户访问http://localhost:8080/demo/hello”为例,拆解Tomcat的请求处理全流程:

  1. 客户端发起请求:用户通过浏览器发送HTTP GET请求,目标地址为http://localhost:8080/demo/hello,其中localhost是主机名,8080是Tomcat的HTTP Connector监听端口,/demo/hello是请求URI。
  2. Connector接收并解析请求
  • 监听8080端口的HTTP Connector接收到请求;
  • 解析HTTP请求行(方法GET、URI/demo/hello、协议HTTP/1.1)、请求头(Host: localhost、User-Agent等);
  • 将解析后的请求信息封装为Tomcat内部的org.apache.catalina.connector.Request对象(实现jakarta.servlet.http.HttpServletRequest接口),同时创建对应的Response对象。
  1. Engine分发请求到Host
  • Connector将Request和Response传递给Engine(Catalina);
  • Engine解析请求头中的Host字段(localhost),匹配到名称为localhost的Host虚拟主机。
  1. Host分发请求到Context
  • Host解析请求URI中的/demo(Context路径),匹配到路径为/demo的Context(Web应用)。
  1. Servlet容器匹配并执行Servlet
  • Context解析URI剩余部分/hello,通过Web应用的映射规则(如web.xml中的<servlet-mapping>@RequestMapping注解),匹配到对应的HelloServlet;
  • 若HelloServlet未初始化(首次访问),Servlet容器调用其init()方法完成初始化(仅执行一次);
  • 调用HelloServlet的service()方法,传入Request和Response对象;
  • HelloServlet在doGet()方法中处理请求(如查询数据、生成响应内容),并将结果写入Response对象。
  1. 返回响应给客户端
  • Servlet处理完成后,Response对象包含响应行(状态码200 OK)、响应头(Content-Type: text/html)、响应体(Hello World!);
  • Connector将Response对象转换为标准的HTTP响应格式,通过8080端口返回给浏览器;
  • 浏览器接收响应后,渲染响应体内容,展示给用户。

四、Tomcat实战:环境搭建与Web应用部署

4.1 Tomcat最新稳定版安装与配置(Windows/Linux通用)
4.1.1 环境准备
  • JDK版本:推荐JDK 17(Tomcat 10.1+要求JDK 8+,JDK 17性能更优);
  • 下载地址:Tomcat 10.1.20(最新稳定版),官网地址:https://tomcat.apache.org/download-10.cgi
  • 系统要求:Windows 10+、Linux CentOS 7+/Ubuntu 20.04+,内存≥2GB。
4.1.2 安装步骤
  1. 下载Tomcat安装包(二进制压缩包,如apache-tomcat-10.1.20-windows-x64.zip);
  2. 解压到自定义目录(如Windows:D:/apache-tomcat-10.1.20,Linux:/usr/local/apache-tomcat-10.1.20);
  3. 配置环境变量(可选,用于全局访问Tomcat命令):
  • Windows:新增系统变量CATALINA_HOME,值为Tomcat解压目录;在Path中添加%CATALINA_HOME%\bin
  • Linux:编辑/etc/profile,添加export CATALINA_HOME=/usr/local/apache-tomcat-10.1.20,执行source /etc/profile生效。
4.1.3 核心配置优化(生产环境必改)
  1. 修改Connector配置conf/server.xml):
    优化线程池和IO模型,提升并发能力:

<Connector port="8080"
          protocol="org.apache.coyote.http11.Http11Nio2Protocol"
          connectionTimeout="30000"
          redirectPort="8443"
          maxThreads="2000"
          minSpareThreads="200"
          acceptCount="1500"
          enableLookups="false"
          URIEncoding="UTF-8"
          maxConnections="10000"
          compression="on"
          compressionMinSize="2048"
          compressableMimeType="text/html,text/xml,text/plain,application/json"/>

  1. 关闭自动部署conf/server.xml的Host标签):
    生产环境关闭autoDeploy,避免误部署风险:

<Host name="localhost"  appBase="webapps"
     unpackWARs="true" autoDeploy="false">

  1. 配置访问日志格式(优化日志可读性和性能):

<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 &quot;%{Referer}i&quot; &quot;%{User-Agent}i&quot;"/>

  1. 其中%D表示请求处理时间(毫秒),便于后续性能分析。
  2. 设置JVM参数bin/catalina.shbin/catalina.bat):优化JVM内存配置,避免OOM和GC频繁:
  • Linux(catalina.sh):在文件开头添加:

JAVA_OPTS="-Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/apache-tomcat-10.1.20/logs/heapdump.hprof"

  • Windows(catalina.bat):在文件开头添加:

set JAVA_OPTS=-Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/apache-tomcat-10.1.20/logs/heapdump.hprof

  1. 参数说明:
  • -Xms2g:初始堆内存2GB;
  • -Xmx2g:最大堆内存2GB(与初始堆一致,避免频繁扩容);
  • -XX:MetaspaceSize:元空间初始大小;
  • -XX:+UseG1GC:使用G1垃圾收集器(适合大内存场景);
  • -XX:+HeapDumpOnOutOfMemoryError:OOM时自动生成堆转储文件,用于故障排查。
4.1.4 启动与验证
  1. 启动Tomcat:
  • Windows:双击bin/startup.bat
  • Linux:执行bin/startup.sh(需添加执行权限:chmod +x bin/*.sh)。
  1. 验证启动成功:
  • 访问http://localhost:8080,若出现Tomcat默认主页,则启动成功;
  • 查看日志:logs/catalina.out(Linux)或logs/catalina.2025-xx-xx.log(Windows),无ERROR日志即为正常。
  1. 停止Tomcat:
  • Windows:双击bin/shutdown.bat
  • Linux:执行bin/shutdown.sh
4.2 Web应用部署的3种核心方式
4.2.1 自动部署(开发环境首选)
  1. 将Web应用的WAR包(如demo.war)或解压后的文件夹复制到Tomcat的webapps目录;
  2. HostautoDeploy="true",Tomcat会自动检测并部署应用;
  3. 访问应用:http://localhost:8080/应用名(WAR包名称即为应用名,如demo.war对应/demo路径)。
4.2.2 配置文件部署(生产环境首选)
  1. conf/Catalina/localhost目录下创建demo.xml文件(文件名即为应用访问路径);
  2. 配置Context信息(如4.2.6节示例),指定docBase为应用的物理路径;
  3. 重启Tomcat(生产环境无autoDeploy时),应用自动部署;
  4. 优势:应用路径与物理路径解耦,便于管理多个应用。
4.2.3 控制台部署(可视化操作)
  1. 启用Tomcat管理控制台:默认情况下,管理控制台未开放,需配置用户权限;
  2. 配置用户(conf/tomcat-users.xml):

<tomcat-users xmlns="http://tomcat.apache.org/xml"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
             version="1.0">

 <user username="admin" password="admin123" roles="manager-gui,admin-gui"/>
</tomcat-users>

  1. 允许远程访问控制台(默认仅允许本地访问,修改webapps/manager/META-INF/context.xml):

<Context antiResourceLocking="false" privileged="true" >
 <!-- 注释掉以下内容,允许远程访问 -->
 <!-- <Valve className="org.apache.catalina.valves.RemoteAddrValve"
        allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->

</Context>

  1. 重启Tomcat,访问控制台:http://localhost:8080/manager/html
  2. 点击“Deploy”,选择WAR包上传并部署。

五、实战案例:基于Tomcat的Spring Boot Web应用开发

5.1 项目架构设计

本案例采用“Spring Boot 3.2 + MyBatis-Plus 3.5 + MySQL 8.0 + Tomcat 10.1”技术栈,开发一个用户管理系统(包含用户查询、新增、修改、删除功能),最终打包为WAR包部署到Tomcat。

5.2 项目初始化(Maven配置)
5.2.1 pom.xml核心依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.5</version>
       <relativePath/>
   </parent>
   <groupId>com.jam.demo</groupId>
   <artifactId>tomcat-demo</artifactId>
   <version>1.0.0</version>
   <name>tomcat-demo</name>
   <description>Tomcat实战demo</description>
   <!-- 打包类型为WAR -->
   <packaging>war</packaging>

   <properties>
       <java.version>17</java.version>
       <mybatis-plus.version>3.5.5</mybatis-plus.version>
       <fastjson2.version>2.0.45</fastjson2.version>
       <lombok.version>1.18.30</lombok.version>
       <springdoc.version>2.3.0</springdoc.version>
   </properties>

   <dependencies>
       <!-- Spring Boot Web Starter(排除内置Tomcat) -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
           <exclusions>
               <exclusion>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-tomcat</artifactId>
               </exclusion>
           </exclusions>
       </dependency>

       <!-- Tomcat依赖(provided,避免打包时包含) -->
       <dependency>
           <groupId>jakarta.servlet</groupId>
           <artifactId>jakarta.servlet-api</artifactId>
           <scope>provided</scope>
       </dependency>

       <!-- Lombok -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>

       <!-- MyBatis-Plus -->
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>

       <!-- MySQL驱动 -->
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <scope>runtime</scope>
       </dependency>

       <!-- FastJSON2 -->
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>

       <!-- SpringDoc(Swagger3) -->
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
           <version>${springdoc.version}</version>
       </dependency>

       <!-- Spring 工具类 -->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context-support</artifactId>
       </dependency>

       <!-- Google Guava(集合工具类) -->
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>33.2.1-jre</version>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <!-- Spring Boot Maven插件 -->
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>

           <!-- 编译插件(指定JDK17) -->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.8.1</version>
               <configuration>
                   <source>17</source>
                   <target>17</target>
                   <encoding>UTF-8</encoding>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

关键说明:

  • 打包类型为war,并排除Spring Boot内置Tomcat(避免与外部Tomcat冲突);
  • 添加jakarta.servlet-api依赖(provided范围,由外部Tomcat提供);
  • 集成Swagger3(SpringDoc)用于接口文档生成;
  • 引入Spring工具类、Google Guava、FastJSON2,符合工具类使用规范。
5.2.2 应用配置文件(application.yml)

spring:
 # 数据库配置
 datasource:
   url: jdbc:mysql://localhost:3306/tomcat_demo?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&characterEncoding=UTF-8
   username: root
   password: root
   driver-class-name: com.mysql.cj.jdbc.Driver
 # 事务管理器(编程式事务)
 transaction:
   default-timeout: 30

# MyBatis-Plus配置
mybatis-plus:
 mapper-locations: classpath:mapper/*.xml
 type-aliases-package: com.jam.demo.entity
 configuration:
   map-underscore-to-camel-case: true
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 global-config:
   db-config:
     id-type: AUTO
     table-prefix: t_

# 服务器配置(部署到Tomcat后,以Tomcat的Connector配置为准)
server:
 servlet:
   context-path: /tomcat-demo
 port: 8080

# SpringDoc(Swagger3)配置
springdoc:
 api-docs:
   path: /api-docs
 swagger-ui:
   path: /swagger-ui.html
   operationsSorter: method
 packages-to-scan: com.jam.demo.controller

# 日志配置
logging:
 level:
   root: INFO
   com.jam.demo: DEBUG
 file:
   name: logs/tomcat-demo.log
 pattern:
   console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
   file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"

5.3 核心代码实现
5.3.1 启动类(War包部署必须继承SpringBootServletInitializer)

package com.jam.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

/**
* 应用启动类(WAR包部署需继承SpringBootServletInitializer)
*
* @author ken
*/

@SpringBootApplication
@MapperScan("com.jam.demo.mapper")
@ComponentScan(basePackages = "com.jam.demo")
public class TomcatDemoApplication extends SpringBootServletInitializer {

   public static void main(String[] args) {
       SpringApplication.run(TomcatDemoApplication.class, args);
   }

}

关键说明:

  • WAR包部署到Tomcat时,必须继承SpringBootServletInitializer,确保Tomcat能正确加载Spring Boot应用;
  • @MapperScan指定MyBatis-Plus的Mapper接口扫描路径。
5.3.2 实体类(User)

package com.jam.demo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* 用户实体类
*
* @author ken
*/

@Data
@Accessors(chain = true)
@TableName("t_user")
public class User implements Serializable {

   private static final long serialVersionUID = 1L;

   /**
    * 主键ID
    */

   @TableId(type = IdType.AUTO)
   private Long id;

   /**
    * 用户名
    */

   private String username;

   /**
    * 密码(加密存储)
    */

   private String password;

   /**
    * 手机号
    */

   private String phone;

   /**
    * 状态:0-禁用,1-正常
    */

   private Integer status;

   /**
    * 创建时间
    */

   private LocalDateTime createTime;

   /**
    * 更新时间
    */

   private LocalDateTime updateTime;

}

5.3.3 Mapper接口(UserMapper)

package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.User;
import org.springframework.stereotype.Repository;

/**
* 用户Mapper接口
*
* @author ken
*/

@Repository
public interface UserMapper extends BaseMapper<User> {

}

5.3.4 服务层(UserService与实现类)

package com.jam.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jam.demo.entity.User;
import com.jam.demo.vo.req.UserAddReq;
import com.jam.demo.vo.req.UserUpdateReq;
import com.jam.demo.vo.resp.UserResp;

import java.util.List;

/**
* 用户服务接口
*
* @author ken
*/

public interface UserService extends IService<User> {

   /**
    * 查询所有用户
    *
    * @return 用户列表
    */

   List<UserResp> listAllUsers();

   /**
    * 根据ID查询用户
    *
    * @param id 用户ID
    * @return 用户详情
    */

   UserResp getUserById(Long id);

   /**
    * 新增用户
    *
    * @param req 新增请求参数
    * @return 新增后的用户ID
    */

   Long addUser(UserAddReq req);

   /**
    * 修改用户
    *
    * @param req 修改请求参数
    */

   void updateUser(UserUpdateReq req);

   /**
    * 根据ID删除用户
    *
    * @param id 用户ID
    */

   void deleteUserById(Long id);

}

实现类(编程式事务):

package com.jam.demo.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.jam.demo.entity.User;
import com.jam.demo.mapper.UserMapper;
import com.jam.demo.service.UserService;
import com.jam.demo.vo.req.UserAddReq;
import com.jam.demo.vo.req.UserUpdateReq;
import com.jam.demo.vo.resp.UserResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
* 用户服务实现类(编程式事务)
*
* @author ken
*/

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

   @Resource
   private UserMapper userMapper;

   @Resource
   private PlatformTransactionManager transactionManager;

   @Override
   public List<UserResp> listAllUsers() {
       log.info("查询所有用户");
       List<User> userList = userMapper.selectList(null);
       if (ObjectUtils.isEmpty(userList)) {
           return Lists.newArrayList();
       }
       // 实体转换为响应VO
       return userList.stream().map(user -> {
           UserResp resp = new UserResp();
           BeanUtils.copyProperties(user, resp);
           return resp;
       }).collect(Collectors.toList());
   }

   @Override
   public UserResp getUserById(Long id) {
       log.info("根据ID查询用户,id:{}", id);
       // 判空校验
       if (ObjectUtils.isEmpty(id)) {
           throw new IllegalArgumentException("用户ID不能为空");
       }
       User user = userMapper.selectById(id);
       if (ObjectUtils.isEmpty(user)) {
           throw new RuntimeException("用户不存在,id:" + id);
       }
       UserResp resp = new UserResp();
       BeanUtils.copyProperties(user, resp);
       return resp;
   }

   @Override
   public Long addUser(UserAddReq req) {
       log.info("新增用户,req:{}", req);
       // 开启事务
       DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
       TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
       try {
           // 实体转换
           User user = new User();
           BeanUtils.copyProperties(req, user);
           user.setStatus(1)
               .setCreateTime(LocalDateTime.now())
               .setUpdateTime(LocalDateTime.now());
           // 插入数据库
           userMapper.insert(user);
           // 提交事务
           transactionManager.commit(status);
           log.info("新增用户成功,userId:{}", user.getId());
           return user.getId();
       } catch (Exception e) {
           // 回滚事务
           transactionManager.rollback(status);
           log.error("新增用户失败", e);
           throw new RuntimeException("新增用户失败", e);
       }
   }

   @Override
   public void updateUser(UserUpdateReq req) {
       log.info("修改用户,req:{}", req);
       // 判空校验
       if (ObjectUtils.isEmpty(req.getId())) {
           throw new IllegalArgumentException("用户ID不能为空");
       }
       // 开启事务
       DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
       TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
       try {
           // 查询用户是否存在
           User user = userMapper.selectById(req.getId());
           if (ObjectUtils.isEmpty(user)) {
               throw new RuntimeException("用户不存在,id:" + req.getId());
           }
           // 复制修改字段
           BeanUtils.copyProperties(req, user);
           user.setUpdateTime(LocalDateTime.now());
           // 更新数据库
           userMapper.updateById(user);
           // 提交事务
           transactionManager.commit(status);
           log.info("修改用户成功,userId:{}", req.getId());
       } catch (Exception e) {
           // 回滚事务
           transactionManager.rollback(status);
           log.error("修改用户失败", e);
           throw new RuntimeException("修改用户失败", e);
       }
   }

   @Override
   public void deleteUserById(Long id) {
       log.info("删除用户,id:{}", id);
       // 判空校验
       if (ObjectUtils.isEmpty(id)) {
           throw new IllegalArgumentException("用户ID不能为空");
       }
       // 开启事务
       DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
       TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
       try {
           // 查询用户是否存在
           User user = userMapper.selectById(id);
           if (ObjectUtils.isEmpty(user)) {
               throw new RuntimeException("用户不存在,id:" + id);
           }
           // 删除用户
           userMapper.deleteById(id);
           // 提交事务
           transactionManager.commit(status);
           log.info("删除用户成功,userId:{}", id);
       } catch (Exception e) {
           // 回滚事务
           transactionManager.rollback(status);
           log.error("删除用户失败", e);
           throw new RuntimeException("删除用户失败", e);
       }
   }
}

关键说明:

  • 采用编程式事务(PlatformTransactionManager),手动控制事务的开启、提交、回滚,符合需求要求;
  • 使用ObjectUtils进行对象判空,符合工具类使用规范;
  • 日志打印使用@Slf4j注解,符合日志规范;
  • 所有方法都进行了参数校验和异常处理,确保代码健壮性。
5.3.5 控制器(UserController)

package com.jam.demo.controller;

import com.jam.demo.service.UserService;
import com.jam.demo.vo.req.UserAddReq;
import com.jam.demo.vo.req.UserUpdateReq;
import com.jam.demo.vo.resp.ApiResponse;
import com.jam.demo.vo.resp.UserResp;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
* 用户管理控制器
*
* @author ken
*/

@RestController
@RequestMapping("/user")
@Tag(name = "用户管理", description = "用户查询、新增、修改、删除接口")
@Slf4j
public class UserController {

   @Resource
   private UserService userService;

   /**
    * 查询所有用户
    *
    * @return 接口响应(用户列表)
    */

   @GetMapping("/listAll")
   @Operation(summary = "查询所有用户", description = "获取系统中所有正常状态的用户列表")
   @ApiResponses({
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功",
                   content = @Content(schema = @Schema(implementation = ApiResponse.class))),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode
= "500", description = "服务器内部错误")
   })
   public ApiResponse<List<UserResp>> listAllUsers() {
       List<UserResp> userList = userService.listAllUsers();
       return ApiResponse.success(userList, "查询成功");
   }

   /**
    * 根据ID查询用户
    *
    * @param id 用户ID
    * @return 接口响应(用户详情)
    */

   @GetMapping("/{id}")
   @Operation(summary = "根据ID查询用户", description = "根据用户ID获取用户详细信息")
   @ApiResponses({
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "查询成功"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "参数错误"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "服务器内部错误")
   })
   public ApiResponse<UserResp> getUserById(
           @Parameter(description = "用户ID", required = true, example = "1")

           @PathVariable Long id) {
       UserResp userResp = userService.getUserById(id);
       return ApiResponse.success(userResp, "查询成功");
   }

   /**
    * 新增用户
    *
    * @param req 新增请求参数
    * @return 接口响应(新增用户ID)
    */

   @PostMapping
   @Operation(summary = "新增用户", description = "新增系统用户,默认状态为正常")
   @ApiResponses({
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "201", description = "新增成功"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "参数错误"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "服务器内部错误")
   })
   @ResponseStatus(HttpStatus.CREATED)
   public ApiResponse<Long> addUser(
           @Parameter(description = "新增用户参数", required = true)

           @RequestBody UserAddReq req) {
       Long userId = userService.addUser(req);
       return ApiResponse.success(userId, "新增成功", HttpStatus.CREATED.value());
   }

   /**
    * 修改用户
    *
    * @param req 修改请求参数
    * @return 接口响应
    */

   @PutMapping
   @Operation(summary = "修改用户", description = "根据用户ID修改用户信息")
   @ApiResponses({
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "修改成功"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "参数错误"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "服务器内部错误")
   })
   public ApiResponse<Void> updateUser(
           @Parameter(description = "修改用户参数", required = true)

           @RequestBody UserUpdateReq req) {
       userService.updateUser(req);
       return ApiResponse.success(null, "修改成功");
   }

   /**
    * 根据ID删除用户
    *
    * @param id 用户ID
    * @return 接口响应
    */

   @DeleteMapping("/{id}")
   @Operation(summary = "删除用户", description = "根据用户ID删除用户")
   @ApiResponses({
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "删除成功"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "参数错误"),
           @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "服务器内部错误")
   })
   public ApiResponse<Void> deleteUserById(
           @Parameter(description = "用户ID", required = true, example = "1")

           @PathVariable Long id) {
       userService.deleteUserById(id);
       return ApiResponse.success(null, "删除成功");
   }

}

5.3.6 通用VO(请求与响应)

  1. 用户响应(UserResp):

package com.jam.demo.vo.resp;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* 用户响应VO
*
* @author ken
*/

@Data
@Schema(description = "用户响应参数")
public class UserResp implements Serializable {

   private static final long serialVersionUID = 1L;

   @Schema(description = "主键ID", example = "1")
   private Long id;

   @Schema(description = "用户名", example = "zhangsan")
   private String username;

   @Schema(description = "手机号", example = "13800138000")
   private String phone;

   @Schema(description = "状态:0-禁用,1-正常", example = "1")
   private Integer status;

   @Schema(description = "创建时间", example = "2025-05-20 10:00:00")
   private LocalDateTime createTime;

   @Schema(description = "更新时间", example = "2025-05-20 11:00:00")
   private LocalDateTime updateTime;

}

5.4 数据库脚本(MySQL 8.0)

-- 创建数据库
CREATE DATABASE IF NOT EXISTS tomcat_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE tomcat_demo;

-- 创建用户表
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_user (
   id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
   username VARCHAR(50) NOT NULL COMMENT '用户名',
   password VARCHAR(100) NOT NULL COMMENT '密码(加密存储)',
   phone VARCHAR(20) NOT NULL COMMENT '手机号',
   status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-正常',
   create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
   update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   PRIMARY KEY (id),
   UNIQUE KEY uk_username (username),
   UNIQUE KEY uk_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

-- 初始化数据
INSERT INTO t_user (username, password, phone, status) VALUES
('zhangsan', 'e10adc3949ba59abbe56e057f20f883e', '13800138000', 1),
('lisi', 'e10adc3949ba59abbe56e057f20f883e', '13800138001', 1);

说明:密码为MD5加密后的"123456",生产环境建议使用BCrypt等更安全的加密方式。

5.5 项目打包与部署
5.5.1 打包WAR包
  1. 执行Maven打包命令:mvn clean package -DskipTests
  2. 打包完成后,在target目录下生成tomcat-demo-1.0.0.war文件;
  3. 重命名为tomcat-demo.war(简化访问路径)。
5.5.2 部署到Tomcat
  1. tomcat-demo.war复制到Tomcat的webapps目录;
  2. 启动Tomcat(若已启动,需重启,因生产环境关闭了autoDeploy);
  3. Tomcat自动解压WAR包,生成tomcat-demo文件夹;
  4. 验证部署成功:
  • 访问Swagger接口文档:http://localhost:8080/tomcat-demo/swagger-ui.html
  • 调用查询接口:http://localhost:8080/tomcat-demo/user/listAll,返回用户列表即为成功。

六、Tomcat性能优化:从配置到调优全攻略

6.1 性能优化核心方向

Tomcat性能优化需从“硬件、JVM、Tomcat配置、应用层”四个维度入手,核心目标是提升并发能力、降低响应时间、避免资源耗尽。

6.2 JVM优化(核心)

JVM是Tomcat运行的基础,不合理的JVM配置会导致GC频繁、OOM、响应延迟等问题,优化配置如下(适配8核16G服务器):

JAVA_OPTS="-Xms8g -Xmx8g \
-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:G1HeapRegionSize=16m \
-XX:G1ReservePercent=20 \
-XX:+ParallelRefProcEnabled \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/usr/local/apache-tomcat-10.1.20/logs/heapdump.hprof \
-XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
-XX:+PrintGCDateStamps \
-Xloggc:/usr/local/apache-tomcat-10.1.20/logs/gc.log \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=5 \
-XX:GCLogFileSize=100M"

参数说明:

  • -Xms8g -Xmx8g:堆内存初始值和最大值设为8G(服务器内存的50%),避免堆扩容;
  • -XX:+UseG1GC:G1收集器适合大内存场景,兼顾吞吐量和延迟;
  • -XX:MaxGCPauseMillis=100:目标最大GC停顿时间100ms,G1会自适应调整;
  • -XX:G1HeapRegionSize=16m:G1堆区域大小,根据堆内存调整(8G堆建议16m);
  • 开启GC日志和堆转储,便于故障排查。
6.3 Tomcat Connector优化

Connector是Tomcat处理请求的入口,优化参数直接影响并发能力,核心配置如下:

<Connector port="8080"
          protocol="org.apache.coyote.http11.Http11Nio2Protocol"
          connectionTimeout="30000"
          redirectPort="8443"
          maxThreads="4000"
          minSpareThreads="500"
          acceptCount="2000"
          maxConnections="20000"
          enableLookups="false"
          URIEncoding="UTF-8"
          compression="on"
          compressionMinSize="2048"
          compressableMimeType="text/html,text/xml,text/plain,application/json,application/javascript,text/css"
          keepAliveTimeout="60000"
          maxKeepAliveRequests="10000"
          acceptorThreadCount="4"
          pollerThreadCount="8"/>

参数说明:

  • maxThreads:最大工作线程数,建议设为“CPU核心数*2 + 有效磁盘数”(8核设为4000);
  • maxConnections:最大连接数(NIO模型下,该值远大于maxThreads,因连接是非阻塞的);
  • keepAliveTimeout:长连接超时时间,设为60s,减少TCP握手开销;
  • maxKeepAliveRequests:单个长连接最大请求数,设为10000,避免长连接占用资源;
  • acceptorThreadCount:接收连接的线程数,建议设为CPU核心数(8核设为4);
  • pollerThreadCount:轮询连接的线程数,建议设为CPU核心数*2(8核设为8)。
6.4 禁用不必要的组件

Tomcat默认加载很多非必需组件,禁用可减少资源占用:

  1. 禁用默认Web应用(docs、examples、host-manager、manager、ROOT):删除webapps目录下的这些文件夹;
  2. 禁用AJP连接器(若未使用Nginx+AJP):注释server.xml中的<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
  3. 关闭Session持久化:在context.xml中添加<Manager pathname=""/>,避免Session写入磁盘。
6.5 应用层优化
  1. 禁用Servlet自动重载:Contextreloadable="false",避免监测文件变化消耗CPU;
  2. 使用连接池:数据库连接池(如HikariCP)、Redis连接池等,避免频繁创建/销毁连接;
  3. 静态资源交由Nginx处理:将CSS、JS、图片等静态资源放在Nginx目录,Tomcat仅处理动态请求;
  4. 接口异步化:使用Spring的@Async或Servlet 3.0异步处理,提升并发能力。

七、Tomcat故障排查:常见问题与解决方案

7.1 常见问题及排查思路
问题现象 可能原因 排查方案
Tomcat启动失败,日志报“Port 8080 already in use” 8080端口被占用 1. 执行`netstat -tlnp
请求响应慢,CPU使用率高 1. JVM GC频繁;2. 应用代码死循环;3. Connector线程数不足 1. 查看GC日志(gc.log),分析GC频率和停顿时间;2. 使用jstack <pid>生成线程快照,排查死循环/锁等待;3. 调整Connector的maxThreads参数
报“OutOfMemoryError: Java heap space” 堆内存不足或内存泄漏 1. 分析堆转储文件(heapdump.hprof),使用MAT工具定位内存泄漏点;2. 调大-Xmx参数;3. 优化应用代码(如关闭未释放的流、缓存合理设置)
中文参数乱码 1. Connector未设置URIEncoding;2. 请求体编码不一致 1. 确保Connector的URIEncoding="UTF-8";2. 应用层统一设置请求编码:request.setCharacterEncoding("UTF-8")
Session丢失 1. Tomcat集群未配置Session共享;2. Session超时时间过短 1. 配置Redis/数据库实现Session共享;2. 调整web.xml<session-config><session-timeout>30</session-timeout></session-config>
7.2 核心排查工具
  1. jps:查看Java进程ID(jps -l);
  2. jstack:生成线程快照,排查死锁、线程阻塞(jstack 1234 > thread.log);
  3. jstat:监控JVM GC状态(jstat -gc 1234 1000,每秒输出一次GC信息);
  4. jmap:生成堆转储文件(jmap -dump:format=b,file=heapdump.hprof 1234);
  5. MAT(Memory Analyzer Tool):分析堆转储文件,定位内存泄漏;
  6. VisualVM:可视化监控JVM状态、线程、内存。

八、Tomcat集群与高可用:生产环境部署方案

8.1 集群架构设计

image.png

8.2 核心配置步骤
8.2.1 Nginx负载均衡配置(nginx.conf)

http {

   upstream tomcat_cluster {

       # 加权轮询,weight越大权重越高

       server 192.168.1.101:8080 weight=10;

       server 192.168.1.102:8080 weight=10;

       server 192.168.1.103:8080 weight=5;

       # 健康检查

       keepalive 32;

   }


   server {

       listen 80;

       server_name www.jam.com;


       # 静态资源缓存

       location ~* \.(css|js|png|jpg|jpeg|gif|ico)$ {

           root /usr/local/nginx/html;

           expires 7d;

           add_header Cache-Control "public, max-age=604800";

       }


       # 动态请求转发到Tomcat集群

       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_connect_timeout 30s;

           proxy_send_timeout 30s;

           proxy_read_timeout 30s;

       }

   }

}

8.2.2 Tomcat Session共享(Redis实现)
  1. 下载Tomcat Redis Session共享插件:tomcat-redis-session-manager-2.0.0.jarjedis-4.4.6.jarcommons-pool2-2.12.0.jar,放入Tomcat的lib目录;
  2. 修改conf/context.xml,添加Redis配置:

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve"/>
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
        host="192.168.1.100"
        port="6379"
        password="redis123"
        database="0"
        maxInactiveInterval="1800"/>

  1. 所有Tomcat节点配置相同的Redis信息,实现Session共享。

九、总结与进阶方向

9.1 核心总结

Tomcat作为Java Web的核心容器,其本质是“HTTP服务器+Servlet容器”的组合体,核心架构分层清晰(Server→Service→Engine→Host→Context),请求处理流程围绕Connector接收、Engine分发、Servlet容器执行展开。开发中需重点关注:

  1. 版本适配:Tomcat 10+使用jakarta.servlet包,需注意与Spring Boot版本兼容;
  2. 配置优化:JVM、Connector、应用层的优化是提升性能的关键;
  3. 故障排查:熟练使用JVM工具定位内存、线程问题;
  4. 生产部署:结合Nginx实现负载均衡,Redis实现Session共享,保证高可用。
9.2 进阶方向
  1. 自定义Tomcat组件:开发自定义Valve(拦截请求)、Connector(自定义协议)、Realm(自定义认证);
  2. Tomcat源码深度解析:阅读Connector、Servlet容器的核心源码,理解底层IO模型(BIO/NIO/NIO2);
  3. 云原生部署:将Tomcat应用打包为Docker镜像,结合K8s实现自动扩缩容、滚动更新;
  4. 性能监控:集成Prometheus+Grafana,监控Tomcat的并发数、响应时间、GC状态等指标。

通过本文的学习,相信你已掌握Tomcat的底层原理、实战配置、性能优化和故障排查的核心技能。在实际开发中,需结合业务场景灵活调整配置,持续优化性能,才能让Tomcat在生产环境中稳定、高效地运行。

目录
相关文章
|
1月前
|
网络协议 Java 数据安全/隐私保护
吃透OSI七层模型:从底层逻辑到实战落地,一文打通网络通信任督二脉
本文从“底层逻辑拆解+权威标准解读+可落地实战示例”三个维度,用通俗的语言讲透OSI七层模型的每一个细节。所有内容均参考ISO/IEC 7498-1官方标准(OSI模型的权威定义),核心论点100%有据可依;实战示例基于Java语言实现,确保可直接编译运行;同时针对易混淆技术点进行明确区分,帮你真正做到“知其然,更知其所以然”。
284 2
|
1月前
|
JSON Java 应用服务中间件
ELK Stack(ES+Logstash+Kibana)全链路通关指南
ELK Stack(Elasticsearch、Logstash、Kibana)作为开源领域最成熟的日志与数据分析解决方案,凭借其高可扩展性、实时性和易用性,被阿里、腾讯、字节跳动等大厂广泛应用于日志收集、业务检索、运维监控等场景。
317 3
|
人工智能 计算机视觉 开发者
阿里Replace Anything:一键替换万物,让图像编辑更简单
【2月更文挑战第17天】阿里Replace Anything:一键替换万物,让图像编辑更简单
1392 1
阿里Replace Anything:一键替换万物,让图像编辑更简单
|
1月前
|
Java 关系型数据库 MySQL
基于springboot的二手物品交易系统
本研究聚焦二手交易平台的网络化转型,探讨其在社会经济快速发展背景下的必要性与意义。结合SpringBoot、Java、MySQL等技术,分析系统设计与实现路径,旨在提升平台管理效率、降低成本,推动二手交易向规范化、信息化发展,助力现代化服务体系建设。
|
1月前
|
存储 算法 Java
告别if-else臃肿代码!策略模式在业务中的落地实践与底层逻辑剖析
策略模式是Java后端开发中解决多分支逻辑问题的“利器”,其核心思想是“封装变化、依赖抽象”,通过抽象策略、具体策略、上下文(或工厂)三个核心角色,实现算法的灵活封装与扩展。
82 1
|
1月前
|
SQL 监控 安全
线程池单例模式实现
本文详解Java中单例线程池的设计与实现,剖析静态内部类、双重检查锁和Spring Bean三种生产级方案,解决资源浪费、管控混乱等问题,提升系统并发性能与稳定性。
88 1
|
7月前
|
JavaScript Linux 内存技术
Debian 11系统下Node.js版本更新方法
Debian 11更新Node.js主要就是这三种方式,无论你是初涉其中的新手还是找寻挑战的专家,总有一种方式能满足你的需求。现在,你已经是这个
781 80
|
7月前
|
API 开发者 Python
ISBN 数据查询 API :掌管书海信息的钥匙 如何调用?
探数平台的ISBN数据查询_专业版API致力于解决高校教辅书籍管理与交易中的痛点。通过输入13位ISBN编码,用户可快速获取图书名称、出版社、作者等20+维度信息,确保正版与版本准确性。该API包含图书基础信息查询和版本对比功能,适用于二手书交易、图书馆管理及电商场景。文档详细解析了ISBN编码规则、接口调用流程及Python示例代码,助力开发者高效集成,推动图书管理数字化升级。
801 0
|
消息中间件 存储 Java
RocketMQ下载安装、集群搭建保姆级教程
RocketMQ下载安装、集群搭建保姆级教程
691 0
|
搜索推荐 NoSQL Java
微服务架构设计与实践:用Spring Cloud实现抖音的推荐系统
本文基于Spring Cloud实现了一个简化的抖音推荐系统,涵盖用户行为管理、视频资源管理、个性化推荐和实时数据处理四大核心功能。通过Eureka进行服务注册与发现,使用Feign实现服务间调用,并借助Redis缓存用户画像,Kafka传递用户行为数据。文章详细介绍了项目搭建、服务创建及配置过程,包括用户服务、视频服务、推荐服务和数据处理服务的开发步骤。最后,通过业务测试验证了系统的功能,并引入Resilience4j实现服务降级,确保系统在部分服务故障时仍能正常运行。此示例旨在帮助读者理解微服务架构的设计思路与实践方法。
778 17