Tomcat 模块

image.png

Tomcat 架构

  1. 连接器(Connector):处理 socket 连接,负责网络字节流与 Request 和 Response 对象的转化;
  2. 容器(Container):加载和管理 Servlet,以及具体处理 Request 请求;

image.png
在 Tomcat 框架中,连接器的实现是 Coyote,容器的实现是 Catalina
image.png

连接器 - Coyote

image.png

容器 - Catalina

image.png

  1. Catalina:负责解析 Tomcat 的配置文件,以此来创建服务器 Server 组件,并根据命令来对其进行管理;
  2. Server:服务器表示整个 Catalina Servlet 容器以及其他组件,负责组装并启动 Servlet 引擎,Tomcat 连接器。Server 通过实现 Lifecycle 接口,提供了一种优雅的启动和关闭整个系统的方式;
  3. Service:Service 是 Server 内部的组件,是 Tomcat 封装的对外提供完整的基于组件的 web 服务。一个 Server 包含多个 Service,各个 Service 之间是独立的,但是共享同一 JVM 资源。它将若干个 Connector 组件绑定到一个 Container(Engine)上对外提供服务;
  4. Connector:连接器,处理与客户端的通信,他负责接收客户请求,然后转给相关的容器处理,最后向客户返回响应结果;
  5. Container:容器,负责处理用户的 Servlet 请求,并返回队形给 Web 用户的模块;

    Container

    image.png

  6. Engine:表示整个 Catalina 的 Servlet 引擎,用来管理多个虚拟站点(Host),一个 Service 最多只能有一个 Engine,但是一个引擎可包含多个 Host;

  7. Host:代表一个虚拟主机或者说虚拟站点,负责 web 应用的部署和 Context 的创建。可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可包含多个 Context;
  8. Context:表示一个Web 应用程序,一个 Web 应用可包含多个 Wrapper;
  9. Wrapper:最底层的容器,是对 Servlet 的封装,负责 Servlet 实例的创建、执行和销毁,Wrapper 作为容器中的最底层,不能包含子容器;

    Tomcat 启动流程

    image.png
    加载 Tomcat 的配置文件,初始化容器组件,监听对应的端口号,接受客户端请求。

    Tomcat 请求处理流程

    image.png
    image.png

  10. Pipeline 用于构建责任链,Valve 代表责任链上的每个处理器。

  11. Pipeline 中维护了一个基础的 Valve,它始终位于 Pipeline 的末端(最后执行),其封装了具体的请求处理和输出响应的过程。
  12. 我们可以调用 addValve() 方法,为 Pipeline 添加其他的 Valve,之后添加的 Valve 位于 基础的 Valve 之前,并按照添加顺序执行。
  13. Pipeline 通过获得首个 Valve 来启动整个责任链上处理器的执行。

    Tomcat 服务器配置 —— server.xml

    Tomcat 配置主要位于 tomcat/conf 目录下的 catalina.policy、cataline.properties、context.xml、server.xml、tomcat-user.xml、web.xml

server.xml 是 Tomcat 服务器的核心配置文件,在 Tomcat 启动时会读取该文件配置,初始化具体的实例 。

详解:https://www.cnblogs.com/kismetv/p/7228274.html

Server

Server 是 server.xml 的根元素,用于创建一个 Server 实例,默认使用的实现类是 org.apache.cataline.core.StandardServer。

  1. <Server port="8005" shutdown="SHUTDOWN">
  2. </Server>
  1. port:Tomcat 监听的关闭服务器的端口;
  2. shutdown:关闭服务器的指令字符串;

Server 内嵌的子元素为 Listener、GlobalNamingResource、Service。

一、默认配置的 5 个 Listener:

<!-- 用于以日志形式输出服务器、操作系统、JVM的版本信息 -->
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />

<!-- 用于加载(服务器启动)和销毁(服务器停止)ARP。如果找不到ARP库,会输出日志,不影响Tomcat启动 -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />

<!-- 用于避免JRE内存泄露  -->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />

<!-- 用于加载(服务器启动)和销毁(服务器停止)全局命名服务  -->
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

<!-- 用于在Context停止时重建Executor线程池中的线程,以避免ThreadLocal相关的内存泄露  -->
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

二、GlobalNamingResource 中定义了全局命名服务:

<GlobalNamingResources>
  <!-- Editable user database that can also be used by
       UserDatabaseRealm to authenticate users
  -->
  <Resource name="UserDatabase" auth="Container"
            type="org.apache.catalina.UserDatabase"
            description="User database that can be updated and saved"
            factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
            pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>

三、Service 在下面小节中介绍。

Service

一个 Tomcat Server 服务器,可以包含多个 Service 服务。一个 Service 代表我们部署在该服务器上的一个 Web 服务;
Service 元素用于创建 Service 实例,默认使用 org.apache.catalina.core.StandardService。默认情况下,Tomcat 仅指定了 Service 的名称,值为“Calalina”。
Service 可以内嵌的元素有 Listener、Executor、Connector、Engine:

  • Listener:用于为 Service 添加声明周期监听器;
  • Executor 用于配置 Service 的共享线程池;
  • Connector 用于配置 Service 包含的连接器;
  • Engine 用于配置 Service 中连接器的对应的 Servlet 容器引擎;
    <Service name="Catalina">
    </Service>
    

    Executor

    默认情况下,Service 并没有配置共享线程池。我们可以通过在 Service 标签下配置 Executor 来配置当前服务的共享线程池,通过在 Connector 标签中指定共享线程池即可(下面介绍 Connector 标签时会介绍)。
    <Executor name="tomcatThreadPool"
            namePrefix="catalina-exec-"
            maxTheads="1000"
            minSpareThreads="100"
            maxIdleTime="60000"
            maxQueueSize="Integer.MAX_VALUE"
            prestartminSpareThreads="false"
            threadPriority="5"
            className="org.apache.catalina.core.StandardThreadExecutor"/>
    
    属性说明:
  1. name:线程池名称,用于 Connector 中指定;
  2. namePrefix:线程池中线程的名称前缀,命名规则为 namePrefix+threadNumber;
  3. maxThreads:线程池的最大线程数;
  4. minSpareThreads:活跃线程数,即核心线程数,这些线程会一直存在不会被销毁;
  5. maxIdleTime:线程空闲时间,超过指定时间,活跃线程数之外的空闲线程会被销毁,默认为 6000(1分钟),单位毫秒;
  6. maxQueueSize:线程等待队列,默认为 int 的最大值;
  7. prestartminSpareThreads:启动线程池时是否预启动 minSpareThreads 指定的线程数,默认为 false,即不启动;
  8. threadPriority:线程池中线程优先级,默认值为 5,值从 1 到 10;
  9. className:线程池的实现类,未指定情况下,默认实现类为 org.apache.catalina.core.StandardThreadExecutor。如果想使用自定义线程池首先需要实现 org.apache.catalina.Executor 接口;

    Connector

    Connector 元素用于创建连接器实例。在 9.x 版本中,默认会配置一个端口为 8080,协议为 HTTP 1.1 的连接器,用于监听端口号为 8080 的 Http 请求(之前版本还会有一个端口为 8009 的AJP 协议的连接器)。

    <Connector port="8080" protocol="HTTP/1.1"
            connectionTimeout="20000"
            redirectPort="8443" />
    

    属性说明:

  10. port:端口号,用于连接器创建服务器 Socket 并进行监听,以等待客户端的请求。当该属性设置为 0,Tomcat 将会随机选择一个可用的端口号给当前 Connector 使用;

  11. protocol:当前 Connector 支持的访问协议。默认为 HTTP/1.1,并采用自动切换机制选择一个基于 Java NIO 的连接器或者基于 APR 的连接器(根据本地是否含有 Tomcat 的本地库判定);

如果不希望采用上述自动切换的机制,可以设置 protocol 属性下方值明确指定要使用的协议:

HTTP协议:
org.apache.coyote.http11.Http11NioProtocol: 非阻塞 Java NIO 连接器
org.apache.coyote.http11.Http11Nio2Protocol: 非阻塞 Java NIO2 连接器
org.apache.coyote.http11.Http11AprProtocol: APR连接器

AJP协议:
org.apache.coyote.ajp.AjpNioProtocol: 非阻塞 Java NIO 连接器
org.apache.coyote.ajp.AjpNio2Protocol: 非阻塞 Java NIO2 连接器
org.apache.coyote.ajp.AjpAprProtocol: APR 连接器

名词解释: HTTP/1.1、AJP、HTTP/2 属于应用层协议 NIO、APR、NIO2 是传输层 IO 模型

  1. executor:指定共享线程池的名称,也可以通过 maxThreads、minSpareThreads 等属性配置内部线程池;如果没指定,连接器 Connector 会有自己的线程池(默认为 10, 命名为 http-nio-8080-exec-num)
  2. connectionTimeOut:Connector 接收连接后的等待超时时间,单位为毫秒,-1 表示不超时;
  3. redirectPort:当前 Connector 不支持 SSL(HTTPS) 请求,接收到一个请求,并且也符合security-constraint 约束,需要 SSL 传输,Catalina 自动将请求重定向到指定的端口;
  4. URIEncoding:用于指定 URI 的字符编码,Tomcat 8.x 版本默认的编码是 UTF-8;

    Engine

    Engine 作为 Servlet 引擎的顶级元素,内部可以嵌入 Host、Cluster、Listener、Realm、Valve;

    <Engine name="Catalina" defaultHost="localhost">
    </Engine>
    
  5. name:用于指定 Engine 的名称,默认为 Catalina,该名称会影响一部分 Tomcat 的存储路径(如临时文件);

  6. defaultHost:默认使用的虚拟主机名称,该项的值为标签 Host 中配置的 name 属性,当客户端请求指向的主机无效时,将交由默认的虚拟主机处理,默认为 localhost。如果指定的是 Host 中配置的域名,当访问 localhost 的时候就会指向 Host 的配置域名;

    Host

    Host 元素用于配置一个虚拟主机,内部可以嵌入 Alias、Cluster、Listener、Valve、Realm、Context。如果在 Engine 下配置了 Realm,那么此配置将在当前 Engine 下的所有 Host 共享。同样,如果在 Host 中配置 Realm,则在当前 Host 下的 Context 中共享。Context 中的 Realm 优先级 > Host 的 Realm 优先级 > Engine 中的 Realm 优先级。

    <Host name="localhost"  appBase="webapps" 
       unpackWARs="true" autoDeploy="true">
    </Host>
    
  7. name:当前 Host 通用的网络名称,必须与 DNS 服务器上的注册信息一致。Engine 中包含的 Host name 必须存在一个与 Engine 的 defaultHost 设置一致的值。

  8. appBase:当前 Host 的应用基础目录,当前 Host 上部署的 Web 应用均在该目录下(可以是绝对目录、相对目录)。默认为 webapps;
  9. unpackWARs:设置为 true,Host 在启动时会将 appBase 中指定目录下的 war 包解压为目录;设置为 false,Host 将直接从 war 文件启动;
  10. autoDeploy:控制 tomcat 是否在运行时定期检测并自动部署新增或变更的 web 应用;

通过给 Host 添加别名,我们可以实现同一个 Host 拥有多个网络名称:

<Host name="www.web1.com"  appBase="webapps" 
      unpackWARs="true" autoDeploy="true">
  <Alias>www.web2.com</Alias>
</Host>

此时,我们就可以通过两个域名访问当前 Host 下的应用(需要确保 DNS 和 hosts 文件中添加的域名的映射配置)。

我们还可以配置多个 Host 虚拟主机,对应不同的应用路径,从而实现部署不同的应用:

<Host name="www.web1.com"  appBase="webapps1" 
      unpackWARs="true" autoDeploy="true">
</Host>

<Host name="www.web2.com"  appBase="webapps2" 
      unpackWARs="true" autoDeploy="true">
</Host>

此时,当我们访问 www.web1.com 的时候就会去 webapps1 下面找应用资源;访问 www.web2.com 的时候就会去 webapps2 下面找应用资源(需要确保 DNS 和 hosts 文件中添加的域名的映射配置)。

Context

Context 元素用于配置一个 Web 应用,默认配置如下:

<Context docBase="myApp" path="/myApp">
</Context>
  1. docBase:Web 应用目录获取 war 包的部署路径,可以是绝对路径,也可以是相对于 Host appBase 的路径;
  2. path:Web 应用的 Context 路口,如果我们 Host 名为 localhost,则 Web 应用访问的根路径为:http://localhost:8080/myApp;

Context 支持的内嵌元素有 CookieProcessor、Loader、Manager、Realm、Resource、WatchedResource、JarScanner、Valve;

默认情况下,server.xml 中并没有对 Context 的配置,这是因为 Tomcat 开启了自动部署,Web 应用由 Tomcat 通过特定的规则自动部署。
自动部署场景下,Tomcat 会自动扫描 appBase 中的 war 包和应用目录,docBase 只有不再 appBase中才需要指定,而 path 属性值则直接根据 appBase 的 war 名称或应用目录名称自动推导出来。

总结

当请求被发送到 Tomcat 所在的主机时,如何确定哪个 Web 应用来处理这个请求呢?

  1. 根据协议和端口号确定 Service 的 Connector,从而确定了 Engine 容器来处理;
  2. 根据域名或 IP 确定 Host;
  3. 根据 URI 上下文确定 Context/Web 应用;
  4. 根据 URI 请求路径确定是哪个 Servlet 处理该请求;

例如:当请求 http://localhost:8080/app1/index.html 时,首先通过协议(http)和端口号(8080)确定 Service;然后通过主机名(localhost)确定 Host;最后通过 URL 上下文(app1)确定 Web 应用目录 app1;最后根据 URL 请求路径(index.html)确定 Servlet;

Web 应用配置 —— web.xml

web.xml 是 web 应用的描述文件,它支持的元素及属性来自于 Servlet 规范定义。在 Tomcat 中,Web 应用的描述信息包括 tomcat/conf/web.xml 中默认配置以及 Web 应用 WEB-INF/web.xml 下的定制配置。

ServletContext 初始化参数

我们可以通过添加 ServletContext 初始化参数,它配置了一个键值对,这样我们可以在应用程序中使用 javax.servlet.ServletContext.getInitParameter()方法获取参数。

配置初始化参数:

<context‐param>
    <param‐name>contextConfigLocation</param‐name>
    <param‐value>classpath:applicationContext‐*.xml</param‐value>
    <description>Spring Config File Location</description>
</context‐param>

获取初始化参数:

HttpServletRequest.getServletContext().getInitParameter("contextConfigLocation");

会话配置

用于配置 Web 应用会话,包括超时时间、Cookie 配置以及会话追踪模式。它将覆盖 server.xml 和 context.xml 中的配置。

<session‐config>
  <session‐timeout>30</session‐timeout>
  <cookie‐config>
    <name>JSESSIONID</name>
    <domain>localhost</domain>
    <path>/</path>
    <comment>Session Cookie</comment>
    <http‐only>true</http‐only>
    <secure>false</secure>
    <max‐age>3600</max‐age>
    </cookie‐config>
  <tracking‐mode>COOKIE</tracking‐mode>
</session‐config>
  1. session‐timeout:会话超时时间,单位分钟
  2. cookie‐config:用于配置会话追踪 Cookie
    1. name:Cookie 的名称
    2. domain:Cookie 的域名
    3. path:Cookie 的路径
    4. comment:注释
    5. http‐only:cookie 只能通过 HTTP 方式进行访问,JS 无法读取或修改,此项可以增加网站访问的安全性。
    6. secure:此 cookie 只能通过 HTTPS 连接传递到服务器,而 HTTP 连接则不会传递该信息。注意是从浏览器传递到服务器,服务器端的 Cookie 对象不受此项影响。
    7. max‐age:以秒为单位表示 cookie 的生存期,默认为 ‐1 表示是会话 Cookie 在浏览器关闭时就会消失。
  3. tracking‐mode:用于配置会话追踪模式,Servlet3.0 版本中支持的追踪模式:COOKIE、URL、SSL
    1. COOKIE : 通过 HTTP Cookie 追踪会话是最常用的会话追踪机制, 而且 Servlet 规范也要求所有的 Servlet 规范都需要支持 Cookie 追踪。
    2. URL : URL 重写是最基本的会话追踪机制。当客户端不支持 Cookie 时,可以采用 URL 重写的方式。当采用 URL 追踪模式时,请求路径需要包含会话标识信息,Servlet 容器会根据路径中的会话标识设置请求的会话信息。如:http://www.myserver.com/user/index.html;jessionid=1234567890。
    3. SSL : 对于 SSL 请求, 通过 SSL 会话标识确定请求会话标识。

      Servlet

      Servlet 的配置主要是两部分, servlet 和 servlet-mapping: ```xml myServlet cn.zsy.web.MyServlet fileName init.conf 1 true

myServlet .do /myservet/


1. servlet‐name : 指定 servlet 的名称, 该属性在 web.xml 中唯一。
1. servlet‐class : 用于指定 servlet 全限定类名;
1. init‐param: 用于指定 servlet 的初始化参数, 在应用中可以通过 HttpServlet.getInitParameter 获取。 
1. load‐on‐startup: 用于控制在 Web 应用启动时,Servlet 的加载顺序。 值小于 0,web 应用启动时,不加载该 servlet, 第一次访问时加载。
1. enabled: true,false。若为 false,表示 Servlet 不处理任何请求。
1. url‐pattern: 用于指定 URL 表达式,一个 servlet‐mapping 可以同时配置多个 url‐pattern。

Servlet 文件上传配置
```xml
<servlet>
  <servlet‐name>uploadServlet</servlet‐name>    
  <servlet‐class>cn.zsy.web.UploadServlet</servlet‐class>    
  <multipart‐config>    
    <location>C://path</location>    
    <max‐file‐size>10485760</max‐file‐size>    
    <max‐request‐size>10485760</max‐request‐size>    
    <file‐size‐threshold>0</file‐size‐threshold>    
  </multipart‐config>    
</servlet>
  1. location:存放生成的文件地址。
  2. max‐file‐size:允许上传的文件最大值。默认值为 ‐1,表示没有限制。
  3. max‐request‐size:针对该 multi/form‐data 请求的最大数量,默认值为 ‐1,表示无限制。
  4. file‐size‐threshold:当数量量大于该值时,内容会被写入文件。

    Listener

    Listener 用于监听 servlet 中的事件,例如 context、request、session 对象的创建、修改、删除,并触发响应事件。Listener 是观察者模式的实现,在 servlet 中主要用于对 context、request、session对象的生命周期进行监控。在 servlet2.5 规范中共定义了 8 种 Listener。在启动时,ServletContextListener 的执行顺序与 web.xml 中的配置顺序一致,停止时执行顺序相反。
    <listener>
     <listener‐class>org.springframework.web.context.ContextLoaderListener</listener‐class>
    </listener>
    

    Filter

    Filter 用于配置 web 应用过滤器,用来过滤资源请求及响应。经常用于认证、日志、加密、数据转换等操作,配置如下: ```xml myFilter
    cn.zsy.web.MyFilter
    true

    language
    CN

myFilter
/*


1. filter‐name:用于指定过滤器名称,在 web.xml 中,过滤器名称必须唯一。
1. filter‐class:过滤器的全限定类名, 该类必须实现 Filter 接口。
1. async‐supported:该过滤器是否支持异步
1. init‐param:用于配置 Filter 的初始化参数,可以配置多个,可以通过 FilterConfig.getInitParameter 获取
1. url‐pattern:指定该过滤器需要拦截的URL。
<a name="GEskx"></a>
# JVM 配置
最常见的 JVM 配置当属内存分配,因为在绝大多数情况下,JVM 默认分配的内存可能不能够满足我们的需求,特别是在生产环境,此时需要手动修改 Tomcat 启动时的内存参数分配。

Windows 环境修改 bin/catalina.bat
```bash
set JAVA_OPTS=‐server ‐Xms2048m ‐Xmx2048m ‐XX:MetaspaceSize=256m
                            ‐XX:MaxMetaspaceSize=256m ‐XX:SurvivorRatio=8

Linux 环境修改 bin/catalina.sh

JAVA_OPTS="‐server ‐Xms1024m ‐Xmx2048m ‐XX:MetaspaceSize=256m
                     ‐XX:MaxMetaspaceSize=512m  ‐XX:SurvivorRatio=8"
  1. -Xms:堆内存的初始大小;
  2. -Xmx:堆内存的最大大小;
  3. -Xmn:新生代的内存大小,官方建议是整个堆内存的 3/8;
  4. -XX:MetaspaceSize:元空间内存初始大小,在 JDK1.8 版本之前配置为 -XX:PermSize(永久代);
  5. -XX:MaxMetaspaceSize:元空间内存最大大小;
  6. -XX:InitialCodeCacheSize/-XX:ReservedCodeCacheSize:代码缓存区大小;
  7. -XX:NewRatio:设置新生代和老年代的相对大小比例,这种方式的优点是新生代会随着整个堆大小动态扩展。如 -XX:NewRatio=3 指定老年代/新生代为 3/1,老年代占堆大小的 3/4,新生代占 1/4;
  8. -XX:SurvivorRatio:指定伊甸园区 (Eden) 与幸存区大小比例。例如 -XX:SurvivorRatio=10 表示伊甸园区(Eden)是幸存区 To 大小的 10 倍 (也是幸存区 From 的 10 倍)。 所以,伊甸园区(Eden)占新生代大小的 10/12,幸存区 From 和幸存区 To 每个占新生代的 1/12 。注意,两个幸存区永远是一样大的。

Tomcat 性能优化

配置调优

通过调整 tomcat/conf/server.xml 中关于连接器的配置可以提升应用服务器的性能:

  1. maxConnections:最大连接处理数,当达到该值后,服务器接收但不会处理更多的请求,额外的请求将会阻塞知道连接数低于 maxConnections。可以通过 ulimit -a 查看服务器限制。
  2. maxThreads:最大线程数;
  3. acceptCount:最大排队等待数,当服务器接收的请求数量达到 maxConnections,此时 Tomcat 会将后面的请求,存放在任务队列中进行排序,acceptCount 指的是任务队列中排队等待的请求数。一台 Tomcat 的最大请求处理数量,是 maxConnections + acceptCount;

    资料

    Tomcat 专题.pdf
    Tomcat 专题_2.pdf