程序猿必须知道的关于 Tomcat 的知识点

身为 Java 程序员,Tomcat 应该算是我们接触的最多的 web 容器了。同时,作为企业生产工具的 “八阿哥” 们,平常只顾着埋头写 BUG,哪有什么时间整理用过的知识点。今天,我将和大家一起梳理一下关于 Tomcat 的相关内容,由浅入深,从入门到放弃。奥利给~!

一、HTTP 请求处理流程

在这里插入图片描述

如上图所示:

  1. 用户通过浏览器发起请求。
  2. 浏览器向目标服务器发起 TCP 连接请求。
  3. 经过三次握手后客户端浏览器与目标服务器成功建立 TCP 连接。(基于 Socket 实现)
  4. 浏览器生成 HTTP 格式的数据包。
  5. 浏览器发送 TCP 请求数据包。(这个数据包的请求头为 TCP 格式的请求头,上一步封装的HTTP 格式的数据包被作为 TCP 请求体传送至服务器)
  6. 服务器解析 TCP 请求体中的 HTTP 数据包。
  7. 服务器处理请求,完成相关业务逻辑。
  8. 服务器生成 HTTP 格式的响应数据包。
  9. 服务器将响应数据包发送至客户端浏览器。
  10. 浏览器解析 HTTP 格式的响应数据包。(解析出的数据为静态数据如:HTMLCSSJS、图片等)
  11. 浏览器渲染响应结果,呈现静态数据给用户。

二、Tomcat 总体架构说明

在这里插入图片描述

Tomcat 作为 Servlet 容器从软件运行角度看,主要可以分为两大模块,一个是 Http 请求接收、响应模块(Coyote),另一个是请求处理模块,即从 Servlet 容器中获取与请求对应的处理方法并执行(Catalina)。

Coyote 连接器包含以下两个模块:

  1. ProtocolHandler 协议处理接口。这个接口通过 EndpointProcessor ,实现针对具体协议的处
    理能⼒。Tomcat 按照协议和 I/O 提供了6个实现类 : AjpNioProtocolAjpAprProtocolAjpNio2ProtocolHttp11NioProtocolHttp11Nio2ProtocolHttp11AprProtocol
  • EndPoint 是用来实现 TCP/IP 协议的,是 Coyote 通信的端点,即通信监听的接⼝。是具体 Socket 接收和发送处理器。EndPoint 是对传输层的抽象。

  • Processor ⽤来实现 HTTP 协议,可将来⾃ EndPointSocket,读取字节流解析成 Tomcat 自己定义的 RequestResponse 对象。Processor 是对应⽤层协议的抽象(注意:这里的 Tomcat RequestResponse 对象,还不是我们在 Servlet 中所用到的 ServletRequestServletResponse 对象。)

  1. Adapter 由于请求协议的不同,客户端发送至后台的请求信息也不尽相同。为了能够转换成统一的 ServletRequestServletResponse 对象方便 Servlet 容器处理业务,则需要该接口的实现类,对发送至 Tomcat 的请求进行统一的适配处理。进而使调用 Servlet 容器中的方法变的简单。

Servlet 容器又名 Catalina。一个 Catalina 实例只能拥有一个 Server 实例,一个 Server 实例可以包含多个 Service 实例。而每一个 Service 实例又可以包含多个 Connector 和一个 Container。一个 Container 只能有一个 Engine,一个 Engine 可以有多个 Host ,每个 Host 可以包含多个 Context (网站),每个 Context 中可以有多个 WrapperServlet)。

  1. Catalina 实例可以被看成就是一个 Tomcat 实例。该实例用以解析 server.xml 配置文件, 以此来创建服务器Server组件并进⾏管理。
  2. Server 表示整个 Catalina Servlet 容器以及其它组件,负责组装并启动 Servlaet 引擎,Tomcat 连接器。
  3. ServiceServer 内部的组件,⼀个 Server 包含多个 Service。它将若⼲个 Connector 组件绑定到⼀个 Container。(通常情况下只用一个 Service 就够了)
  4. Container 负责处理⽤户的 servlet 请求,并将处理结果返回给用户。
  • Engine 是整个 CatalinaServlet 引擎,⽤来管理多个虚拟站点,⼀个 Service 最多只能有⼀个 Engine,但是⼀个 Engine 可包含多个 Host
  • Host 代表⼀个虚拟主机,或者说⼀个站点,可以给 Tomcat 配置多个虚拟主机地址。⽽⼀个虚拟主机下可包含多个 Context
  • Context 表示⼀个 Web 应⽤程序, ⼀个 Web 应⽤可包含多个 Wrapper
  • Wrapper 表示⼀个 ServletWrapper 作为容器中的最底层,不能包含⼦容器。

Tips

上述组件的配置其实就体现在 Tomcat 软件中的 conf/server.xml 里。

三、Tomcat 核心配置文件 server.xml

问个实在的问题:当你第一次甚至说上一次看 Tomcat 的配置文件 server.xml 时,你的第一感受是啥?反正我最真切的感受就是:“我靠,英文真多~!”。不过静下心来再看,更多的都是说明性文字。删除注释后再看,就跟被拔光毛的鸡一样,也就那样。不信你看

<?xml version="1.0" encoding="UTF-8"?>
<!-- port:关闭服务器的监听端⼝  shutdown:关闭服务器的指令字符串 -->
<Server port="8005" shutdown="SHUTDOWN">
	 <!-- 以⽇志形式输出服务器 、操作系统、JVM的版本信息 -->
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <!-- 加载(服务器启动)和销毁(服务器停⽌)APR。如果找不到 APR 库,则会输出⽇志,但并不影响 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" />
    <!-- 全局命名服务,定义服务器的全局 JNDI 资源 -->
    <GlobalNamingResources>
        <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 name="Catalina">
    	<!-- 处理 HTTP/1.1 协议的请求 -->
        <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
                   redirectPort="8443" />
        <!-- 处理 AJP/1.3 协议的请求 用不到的话可以注释掉 -->
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
        
        <Engine name="Catalina" defaultHost="localhost">
            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm" 
                       resourceName="UserDatabase" />
            </Realm>
            <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
                <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" />
            </Host>
        </Engine>
    </Service>
</Server>

这么一整理是不是瞬间清爽了很多。需要注意的是,通常情况下,上面配置文件里添加了注释的部分,除了 Connector 标签中的参数,其他标签的内容一般不需要修改,保持默认即可。

四、核心配置文件进阶

4-1 <Service> 标签

<Service name="Catalina">
  ...
</Service>

该标签⽤于创建 Service 实例,默认使⽤ org.apache.catalina.core.StandardService 对象。默认情况下,Tomcat 仅指定了 Service 的名称, 值为 “Catalina”。
Service ⼦标签有 : ListenerExecutorConnectorEngine。其中:

  • Listener ⽤于为 Service 添加⽣命周期监听器(用的比较少,故下文不做说明)
  • Executor ⽤于配置 Service 共享线程池
  • Connector ⽤于配置 Service 包含的链接器
  • Engine ⽤于配置 Service 中链接器对应的 Servlet 容器引擎

4-2 <Executor> 标签

该标签用于为 Service 添加线程池。默认情况下,在 server.xml 文件中,该标签是被注释掉的,即未开启线程池的。如果想开启,则打开注释,并配置相关属性即可。

<Executor name="commonThreadPool"
    namePrefix="thread-exec-"
    maxThreads="200"
    minSpareThreads="100"
    maxIdleTime="60000"
    maxQueueSize="Integer.MAX_VALUE"
    prestartminSpareThreads="false" 
    threadPriority="5"
    className="org.apache.catalina.core.StandardThreadExecutor"/>

<Executor> 标签属性含义

  • name:线程池名称。通过该属性的值,可在 <Connector> 标签中指定对应线程池。
  • namePrefix:所创建的每个线程的名称前缀,⼀个单独的线程名称为:namePrefix + threadNumber
  • maxThreads:线程池中最⼤线程数。
  • minSpareThreads:最小空闲线程数,即活跃线程数,也就是核⼼池线程数。这些线程不会被销毁,会⼀直存在。
  • maxIdleTime:线程空闲时间,超过该时间后,空闲线程会被销毁,默认值为 6000(1分钟),单位毫秒。
  • maxQueueSize:在被执⾏前最⼤线程排队数⽬,默认为 Int 的最⼤值,也就是⼴义的⽆限。除⾮特殊情况,否则这个值不需要更改,不然会有请求不会被处理的情况发⽣。
  • prestartminSpareThreads:启动线程池时是否启动 minSpareThreads 部分线程。默认值为 false,即不启动。
  • threadPriority:线程池中线程优先级,默认值为 5,值从 110
  • className:线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor。如果想使⽤⾃定义线程池⾸先需要实现org.apache.catalina.Executor 接⼝。

4-3 <Connector> 标签

该⽤于创建链接器实例默认情况下,server.xml 配置了两个链接器,⼀个⽀持 HTTP 协议,⼀个⽀持 AJP 协议⼤多数情况下,我们并不需要新增链接器配置,只是根据需要对已有链接器进⾏优化。

<Connector port="8080"
          protocol="HTTP/1.1"
          connectionTimeout="20000"
          redirectPort="8443"
          executor="commonThreadPool"
          URIEncoding="UTF-8"
          minSpareThreads="100"
          maxThreads="1000"          
          acceptCount="1000"
          maxConnections="1000"
          compression="on"
          compressionMinSize="2048"
          disableUploadTimeout="true"
           /> 

<Connector> 标签属性含义

  • port:端⼝号,Connector ⽤于创建服务端 Socket 并进⾏监听, 以等待客户端请求链接。如果该属性设置为 0Tomcat 将会随机选择⼀个可⽤的端⼝号给当前 Connector 使⽤。
  • protocol:当前 Connector ⽀持的访问协议。 默认为 HTTP/1.1,并采⽤⾃动切换机制选择⼀个基于JAVA NIO 的链接器或者基于本地 APR 的链接器。(若想使用 APR 提高 Tomcat 的并发性能,则需要安装相关运行库后,再进行配置修改)
  • connectionTimeOutConnector 接收链接后的等待超时时间, 单位为 毫秒。 -1 表示不超时。
  • redirectPort:当前 Connector 不⽀持 SSL 请求。如果接收到了⼀个请求, 且同时符合 security-constraint 约束,需要 SSL 传输,Catalina 则会⾃动将请求重定向到指定的端⼝。
  • executor:指定要使用的共享线程池的名称。
  • URIEncoding:⽤于指定编码 URI 的字符编码,Tomcat8.x 版本默认的编码为 UTF-8, Tomcat7.x版本默认为 ISO-8859-1
  • 当我们不使用共享线程池(即不添加 executor 属性),选择让每一个 Connector 单独维护自己的线程池时可通过 maxThreadsminSpareThreadsacceptCountmaxConnections 进行 Connector 线程池的配置。需要注意的是 maxThreadsacceptCountmaxConnections 这三个属性的值通常保持一致。
  • compression:是否开启数据请求等的 gzip 格式压缩。
  • compressionMinSize:最小压缩大小,即当数据请求超过指定值后才会进行压缩。
  • disableUploadTimeout:允许 Servlet 容器,使用较长的连接超时值,以使 Servlet 有较长的时间来完成它的执行,默认值为 false

4-4 <Engine> 标签

<Engine name="Catalina" defaultHost="localhost">
  ...
</Engine>

该标签表示 Servlet 引擎。其属性 name ⽤于指定 Engine 的名称, 默认为CatalinadefaultHost 属性用于指定默认使⽤的虚拟主机名称,当客户端请求指向的主机⽆效时,将交由默认的虚拟主机处理,默认为值 localhost

4-5 <Host> 标签

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

该标签用于配置⼀个虚拟主机。

  • name:指定虚拟主机的名称。
  • appBase:指定引用基础目录。
  • unpackWARs:是否自动解压 war 包。
  • autoDeploy:当资源有变更时是否自动部署。

4-6 <Context> 标签

<Host name="www.abc.com"  appBase="webapps" unpackWARs="true" autoDeploy="true">
    <Context docBase="/Users/yingdian/web_demo" path="/web_demo"></Context>  
    <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" />
</Host>

<Host> 标签中的 <Context> 标签用于配置一个 Web 应用。

  • docBase:指定 Web 应⽤⽬录或者 War 包的部署路径。可以是绝对路径,也可以是相对于 Host appBase 的相对路径。(appBase 的子目录)
  • pathWeb 应⽤的 URL 访问路径。如上所示,我们 Host 名为 www.abc.com, 则该 Web 应⽤访问的根路径为:http://www.abc.com:8080/web_demo

五、Tomcat 配置 HTTPS

  1. 使⽤ JDK 中的 keytool ⼯具⽣成免费的秘钥库⽂件(证书)。

    keytool -genkey -alias SupremeSir -keyalg RSA -keystore supreme.keystore 
    

在这里插入图片描述

  1. 配置 server.xml
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" schema="https" secure="true" SSLEnabled="true">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="E:\**\apache-tomcat\bin\supreme.keystore" 					     certificateKeystorePassword="123456"  type="RSA"/>
    </SSLHostConfig>
</Connector>

certificateKeystoreFile:指定证书所在位置。

certificateKeystorePassword:指定证书秘钥库口令。

六、Tomcat 性能优化

Tomcat 优化没有明确的参数值可以直接去使⽤,必须根据⾃⼰的真实⽣产环境来进⾏调整,调优是⼀个过程。调优主要从两个⽅⾯进⾏:

  1. JVM 虚拟机优化(优化内存模型)
  2. Tomcat ⾃身配置的优化

6-1 Java 虚拟机内存相关参数

参数 参数作用 优化建议
-server 启动Server,以服务端模式运⾏ 服务端模式建议开启
-Xms 最⼩堆内存 建议与-Xmx设置相同
-Xmx 最⼤堆内存 建议设置为可⽤内存的80%
-XX:MetaspaceSize 元空间初始值 自行调整
-XX:MaxMetaspaceSize 元空间最⼤内存 默认⽆限
-XX:NewRatio 年轻代和⽼年代⼤⼩⽐值,取值为整数,默认为2 不需要修改
-XX:SurvivorRatio Eden区与Survivor区⼤⼩的⽐值,取值为整数,默认为8 不需要修改

bin/catalina.sh 的脚本中 , 追加如下配置

JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

6-2 垃圾收集器

  1. 串⾏收集器(Serial Collector

    单线程执⾏所有的垃圾回收⼯作, 适⽤于单核CPU服务器。

  2. 并⾏收集器(Parallel Collector

    ⼜称为吞吐量收集器(关注吞吐量), 以并⾏的⽅式执⾏年轻代的垃圾回收, 该⽅式可以显著降
    低垃圾回收的开销(指多条垃圾收集线程并⾏⼯作,但此时⽤户线程仍然处于等待状态)。适⽤于多
    处理器或多线程硬件上运⾏的数据量较⼤的应⽤。

  3. 并发收集器(Concurrent Collector

    以并发的⽅式执⾏⼤部分垃圾回收⼯作,以缩短垃圾回收的暂停时间。适⽤于那些响应时间优先于
    吞吐量的应⽤, 因为该收集器虽然最⼩化了暂停时间(指⽤户线程与垃圾收集线程同时执⾏,但不⼀
    定是并⾏的,可能会交替进⾏), 但是会降低应⽤程序的性能。

  4. CMS 收集器(Concurrent Mark Sweep Collector

    并发标记清除收集器, 适⽤于那些更愿意缩短垃圾回收暂停时间并且负担的起与垃圾回收共享处
    理器资源的应⽤

  5. G1 收集器(Garbage-First Garbage Collector

    适⽤于⼤容量内存的多核服务器, 可以在满⾜垃圾回收暂停时间⽬标的同时, 以最⼤可能性实现
    ⾼吞吐量(JDK1.7 之后)

    垃圾回收器参数

    参数 描述
    -XX:+UseSerialGC 启⽤串⾏收集器
    -XX:+UseParallelGC 启⽤并⾏垃圾收集器,配置了该选项,那么 -XX:+UseParallelOldGC默认启⽤
    -XX:+UseParNewGC 年轻代采⽤并⾏收集器,如果设置了 -XX:+UseConcMarkSweepGC选项,⾃动启⽤
    -XX:ParallelGCThreads 年轻代及⽼年代垃圾回收使⽤的线程数。默认值依赖于JVM使⽤的CPU个数
    -XX:+UseConcMarkSweepGC 对于⽼年代,启⽤CMS垃圾收集器。 当并⾏收集器⽆法满⾜应⽤的延迟需求时,
    推荐使⽤CMS或G1收集器。启⽤该选项后,-XX:+UseParNewGC⾃动启⽤。
    -XX:+UseG1GC 启⽤G1收集器。 G1是服务器类型的收集器, ⽤于多核、⼤内存的机器。
    它在保持⾼吞吐量的情况下,⾼概率满⾜GC暂停时间的⽬标。

    bin/catalina.sh 的脚本中 , 追加如下配置:

    JAVA_OPTS="-XX:+UseConcMarkSweepGC"
    

    如果存在虚拟机内存相关参数配置,则直接在 JAVA_OPTS 中追加即可。

6-3 Tomcat 配置调优

  1. 启用 Tomcat 线程池

  2. 调整 Tomcat 的连接器

    调整 Tomcat/conf/server.xml 中关于链接器的配置可以提升应⽤服务器的性能。

    参数 说明
    maxConnections 最⼤连接数,当到达该值后,服务器接收但不会处理更多的请求, 额外的请求将会阻塞直到连接数低于maxConnections 。可通过ulimit -a 查看服务器限制。对于CPU要求更⾼(计算密集型)时,建议不要配置过⼤ ; 对于CPU要求不是特别⾼时,建议配置在2000左右(受服务器性能影响)。 当然这个需要服务器硬件的⽀持
    maxThreads 最⼤线程数,需要根据服务器的硬件情况,进⾏⼀个合理的设置
    acceptCount 最⼤排队等待数,当服务器接收的请求数量到达maxConnections ,此时Tomcat会将后⾯的请求,存放在任务队列中进⾏排序, acceptCount指的就是任务队列中排队等待的请求数 。 ⼀台Tomcat的最⼤的请求处理数量,是maxConnections+acceptCount
  3. 禁⽤ AJP 连接器
    在这里插入图片描述

  4. 调整 IO 模式

    Tomcat8 之前的版本默认使⽤ BIO(阻塞式 IO),对于每⼀个请求都要创建⼀个线程来处理,不适合⾼并发;Tomcat8 以后的版本默认使⽤ NIO 模式(⾮阻塞式 IO)。

    在这里插入图片描述
    Tomcat 并发性能有较⾼要求或者出现瓶颈时,我们可以尝试使⽤ APR 模式,APRApache Portable Runtime)是从操作系统级别解决异步 IO 问题,使⽤时需要在操作系统上安装 APRNative(因为 APR 原理是使⽤使⽤ JNI 技术调⽤操作系统底层的 IO 接⼝)。

  5. 动静分离

    可以使⽤ Nginx + Tomcat 相结合的部署⽅案,Nginx 负责静态资源访问,Tomcat 负责 Jsp 等动态资
    源访问处理(因为 Tomcat 不擅⻓处理静态资源)。

源码

源码下载

------------------------------------- 保持热爱,翻阅山海。 -------------------------------------

猜你喜欢

转载自blog.csdn.net/Supreme_Sir/article/details/111027495
今日推荐