Tomcat 底层原理与实战全解析

从入门到精通:Tomcat底层原理与实战全解析

引言:为什么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 完整请求处理流程图
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对象。

  3. Engine分发请求到Host

    • Connector将Request和Response传递给Engine(Catalina);

    • Engine解析请求头中的Host字段(localhost),匹配到名称为localhost的Host虚拟主机。

  4. Host分发请求到Context

    • Host解析请求URI中的/demo(Context路径),匹配到路径为/demo的Context(Web应用)。
  5. Servlet容器匹配并执行Servlet

    • Context解析URI剩余部分/hello,通过Web应用的映射规则(如web.xml中的<servlet-mapping>@RequestMapping注解),匹配到对应的HelloServlet;

    • 若HelloServlet未初始化(首次访问),Servlet容器调用其init()方法完成初始化(仅执行一次);

    • 调用HelloServlet的service()方法,传入Request和Response对象;

    • HelloServlet在doGet()方法中处理请求(如查询数据、生成响应内容),并将结果写入Response对象。

  6. 返回响应给客户端

    • 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"/>
  2. 关闭自动部署conf/server.xml的Host标签):

    生产环境关闭autoDeploy,避免误部署风险:

    复制代码
    <Host name="localhost"  appBase="webapps"
          unpackWARs="true" autoDeploy="false">
  3. 配置访问日志格式(优化日志可读性和性能):

    复制代码
    <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;"/>

    其中%D表示请求处理时间(毫秒),便于后续性能分析。

  4. 设置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

    参数说明:

    • -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)。

  2. 验证启动成功:

    • 访问http://localhost:8080,若出现Tomcat默认主页,则启动成功;

    • 查看日志:logs/catalina.out(Linux)或logs/catalina.2025-xx-xx.log(Windows),无ERROR日志即为正常。

  3. 停止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>
  3. 允许远程访问控制台(默认仅允许本地访问,修改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>
  4. 重启Tomcat,访问控制台:http://localhost:8080/manager/html

  5. 点击"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 集群架构设计
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配置:

  3. 所有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在生产环境中稳定、高效地运行。

相关推荐
小张同学a.4 天前
zabbix企业级监控平台3——服务监控与API实战
linux·运维·mysql·nginx·tomcat·zabbix
xxwl58510 天前
数据库后端接口测试报告
spring boot·mysql·tomcat
次次皮12 天前
Linux 下通过 XShell 手动更新 Tomcat 应用并重启
前端·tomcat·jar
Listen·Rain13 天前
用AI开发出一个AI
java·人工智能·spring boot·tomcat·intellij-idea·mybatis·visual studio
Full Stack Developme14 天前
SpringBoot 内嵌 Tomcat 的启动流程
spring boot·后端·tomcat
Full Stack Developme14 天前
Tomcat 如何处理HTTP请求
java·http·tomcat
Full Stack Developme15 天前
Tomcat 设计原理
java·tomcat
Full Stack Developme15 天前
Tomcat NIO连接器内部如何实现零拷贝 Session管理如何实现集群同步
java·tomcat·nio
BullSmall16 天前
Tomcat 部署 Jenkins → Docker Jenkins 完整迁移方案
docker·tomcat·jenkins
baozhengw17 天前
信创改造-SpringBootJar包改为WAR包部署Tomcat
java·tomcat