avatar

Tomcat高级使用及其原理剖析

Tomcat系统架构与原理剖析

浏览器处理服务器的流程

Tomcat系统总体架构

Tomcat请求处理大概流程

Tomcat有两个身份:

  • Tomcat是一个http容器
  • Tomcat还是一个Servlet容器

    Tomcat Servlet容器处理流程

    当用户请求某个URL资源时
    1) HTTP服务器会把请求信息使用ServletRequest对象封装起来
    2) 进一步去调用Servlet容器中某个具体的Servlet
    3) 在2)中,Servlet容器拿到请求后,根据URL和servlet的映射关系,找到对应的Servlet
    4) 如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的init方法来完成初始化
    5) 接着调用这个具体的Servlet的ervice方法去处理请求,请求处理结果使用ServletResponse对象封装
    6) 把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端

    Tomcat总体架构综述

    Tomcat有两个非常重要的功能需要完成
    1) 和客户端浏览器进行交互,进行socket通信,将字节流和Request/Response等对象进行转换
    2) Servlet容器处理业务逻辑
    Tomcat设计了两个核心组件连接器(Connector)容器(Container)来完成Tomcat的两大核心功能。
  • 连接器,负责对外交流: 处理Socket连接,负责网络字节流和Request和Responese对象的转化;
  • 容器,负责内部处理:加载和管理Servlet,以及具体处理Request请求;

Tomcat连接器组件Coyote

Coyote简介

Coyote是Tomcat中连接器的组件名称,是对外的接口。客户端通过Coyote与服务器建立连接、发送请求并接受响应

  • Coyote封装了底层的网络通信(Socket请求及响应处理)
  • Coyote使Catalina容器与具体的请求协议以及IO操作方式完全解耦
  • Coyote将Socket输入转换封装为Request对象,进一步封装后交由Catalina容器进行处理,处理请求完成后,Catalina通过Coyote提供的Response对象将结果写入输出流
  • Coyote负责的是具体协议(应用层)和IO(传输层)相关内容
    Tomcat Coyote支持的I/O模型与协议,Tomcat支持多种应用程序协议和I/O模型,如下:
    应用层协议:
  • HTTP/1.1 , 这是大部分web应用采用的访问协议
  • AJP 用于和WX集成(Apache),以实现对静态资源的优化以及集群部署,当前支持AJP/1.3
  • HTTP/2 Http 2.0 大幅度的提升了Web性能。下一代HTTP协议,自8.5以及9.0版本之后支持
    IO模型:
  • NIO 非阻塞I/o 采用java NIO 类库实现
  • NIO2 异步I/O 采用JDK7最新的NIO2类库实现
  • APR 采用Apcahe可移植运行库实现,是C/C++编写的本地库,如果选择该方案,需要单独安装APR库

在8.0之前,Tomcat默认采用的I/O方式为BIO,之后改为了NIO。无论是NIO、NIO2还是APR,在性能方面均优于以往的BIo。如果采用APR,甚至可以达到Apache HTTP Server的性能。

Coyote的内部组件及流程

  • Coyote组件及其作用
    组件 作用描述
    Endpoint EndPoint是Coyote通信端点,即通信监听的接口,是具体Socket接收和发送处理器,是对传输层的抽象,因此EndPoint用来实现TCP/IP协议的

|Processor|Processor是Coyote协议处理接口,如果说endPoint是用来实现TCP/IP协议的,那么Processor用来实现HTTP协议的,Processor接收来自EndPoint的Socket,读取字节流解析成Tomcat Request和Response对象,并通过Adapter将其提交给容器处理,Processor是对应应用层协议的抽象|

|ProtocolHandler|Coyote协议接口,通过EndPint和Processor,实现针对具体协议的处理能力。Tomcat按照协议和I/O提供的六个实现类:AjpNioProtocol,AjpAprProtocol,AjpNio2Protocol,Http11NioProtocol,Http11Nio2Protocol,Http11AprProtocol|

|Adapter|由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request类来封装这些请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Request类。但是这个Request对象不是标准的ServletRequest,不能用Tomcat Request作为参数来调用容器。Tomcat设计者的解决方案是引入了CoyoteAdapter,这是适配器模式的经典运用,连接器调用CoyoteAdpater的Service方法,传入的是Tomcat Request对象。CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调用容器|

Tomcat Servlet容器 Catalina

Tomcat模块分层结构图及Catalina位置

Tomcat是一个由一系列可配置(conf/server.xml)的组件构成的web容器,而Catalina是Tomcat的Servlet容器。
从另一个角度来说,Tomcat本质上就是一款Servlet容器,因此Catalina才是Tomcat的核心,其他模块都是Catalina提供支撑的。比如:通过Coyote模块提供链接通信,Jasper模块提供JSP引擎,Naming提供命名服务,Juli提供日志服务。

Servlet容器Catalina的结构

其实,可以认为整个Tomcat就是一个Catalina实例,Tomcat启动的时候会初始化这个实例,Catalina实例通过加载server.xml完成其他实例的创建,创建并管理server,Server创建并管理多个服务,每个服务又可以有多个Connector和一个Container。一个Catalina实例对应一个Server实例,一个server实例对应多个Service实例,每一个service实例下可以有多个Connector实例和一个Container实例。

  • Catalina:负责解析Tomcat的配置文件(server.xml),以此来创建服务器Server组件并管理
  • Server:服务器表示整个Catalina Servlet容器以及其他组件,负责组装并启动Servlet引擎,Tomcat连接器。Server通过实现Lifecycle接口,提供一种优雅的启动和关闭整个系统的方式
  • Service:服务是Server内部的组件,一个Server包含多个Service。它将若干个Connector组件绑定到一个Container
  • Container:容器,负责处理用户的servlet请求,并返回对象给web用户的模块

Container组件的具体结构

Container组件有几种具体的组件,分别是Engine、Host、Context和Wrapper。这四种组件(容器)是父子关系。Tomcat通过一种分层的架构,使得Servlet容器具有很好的灵活性。

  • Engine:表示整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个service最多只能有一个Engine,但是一个引擎可包含多个Host
  • Host:标识一个虚拟足迹,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可包含多个Context
  • Context:标识一个web应用程序,一个web应用可包含多个Wrapper
  • Wrapper:表示一个Servlet,Wrapper作为容器中的最底层,不能包含子容器
    这些关系的配置其实就体现在conf/server.xml中

Tomcat服务器核心配置详解

主要标签结构如下:

<!--
Server根元素,创建一个Server实例,子标签有Listener、GlobalNamingResources、Service
-->
<Server>
<!--定义监听器-->
<Listener/>
<!--定义服务器的全局JNDI资源-->
<GlobalNamingResources/>
<!--
定义一个Service服务,一个Server标签可以有多个Service服务实例
-->
<Service/>
</Server>

Server标签

<!--
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停止时创建重建Excutor池中的线程,以避免TnreadLocal相关的内存泄漏-->
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

<!--
GlobalNamingResources 中定义了一个全局命名服务
-->
<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">
...
</Service>
</Server>

Service标签

<!--
该标签用于创建Service实例,默认使用org.apache.catalina.core.StandardService。
默认情况下,Tomcat仅指定Service的名称,值为"Catalina"。
Service字标签为:Listener、Excutor、Connector、Engine
其中:
Listener 用于为Sercice添加生命周期监听器
Executor 用于配置Service共享线程池
Connector 用于配置Service包含的链接器
Engine 用于配置Service中链接器对应的Servlet容器引擎
-->
<Service name = "Catalina">
...
</Service>

Executor标签

Executor标签主要是创建一个线程池,当Connector监听到端口的消息时,可以从Executor线程池中那到线程去处理。

<!--
默认情况下Service并未添加共享线程池配置,如果我们想添加一个线程池,可以在Service下添加如下配置
name: 线程池名称,用于Connector中指定
namePrefix:所创建的每个线程的名称前缀,一个单独的线程名称为 namePrefix + threadNumber
maxThreads:池中的最大线程数
minSpareThreads: 活跃线程数,也就是核心线程池数,这些线程不会被销毁,会一直存在
maxIdleTime:线程空闲时间,超过该时间后,空闲线程会被销毁,默认值为6000(1分钟),单位为毫秒
maxQueueSize:在被执行前最大线程排队数目,默认为Int的最大值,也就是广义的无限。除非特殊情况,这个值不需要更改,否则会有请求不会被处理的情况发生
prestartminSpareThreads: 启动线程池时是否启动,minSpareThreads部分线程,默认值为false,即不启动
threadPriority:线程池中线程优先级,默认为5,值为1-10
ClassName : 线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StabsardThreadExcutor。如果想要自定义线程池首先需要实现org.apache.catalina.Excutor接口
-->
<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"
/>

Connector标签

Connector标签用于创建链接器实例
默认情况下,server.xml配置了两个链接器,一个支持HTTP协议,一个支持AJP协议
大多数情况下,我们并不需要新增链接器配置,只需要根据对已有的链接器进行优化

<!--
port: 端口号,Connector用于创建服务器Socket并进行监听,以等待客户端请求连接,如果该属性设置为0,Tomcat会随机选择一个可用的端口号给当前Connector使用
porocol:当前Connector支持的访问协议,默认为Http/1.1 并采用自动切换机制选择一个基于java NIO的链接器或者基于本地APR的链接器(根据本地是否含有Tomcat的本地库判定)
connectionTimeout:Connector接收链接后的等待超时时间,单位为毫秒 -1表示不超时
redirectPort:当前Connector不支持SSL请求,接收到了一个请求并且也符合security-constraint约束,需要SSL传输,Catalina自动将请求重定向到指定的端口
executor: 指定共享线程池的名称,也可以通过maxThreads、minSpareThreads等属性配置内部线程池
URIEncoding:用于指定编码URI的字符编码,Tomcat8.x版本默认的编码为UTF-8 Tomcat7.x版本默认为ISO-8859-1
compression:要不要开启gzip压缩
compressionMinSize = "2048" 意思是超过2048kb以后才进行压缩
disableUploadTimeout = "true" 为文件上传等接口延长一些超时时间
-->
<Connector port="8080" portocol="HTTP/1.1" connectionTimeout = "2000" redirectPort = "8443">
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Engine标签

Engine表示Servlet引擎

<!--
name : 用于指定engine的名称,默认为Catalina
defaultHost:默认使用的虚拟主机名称,当客户端请求指向的主机无效时,将交由默认的虚拟主机处理,默认为localhost
-->
<Engine name = "Calalina" defaultHost="localhost">
...
</Engine>

Host标签

Host标签用于配置一个虚拟主机

<!--
name: host的名字,可以叫www.abc.com
appBase:应用所在的目录结构是什么
unpackWARs:是否自动解压war包
autoDeploy:是否自动部署
-->
<Host name="localhost" appBase="webapps" unpackWARs = "true" autoDeploy = "true">
<!--
Valve:阀门,使用log日志将一些请求处理信息记录下来
-->
<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>

Context标签

Context标签用于配置一个web应用

<Host name="localhost" appBase="webapps" unpackWARs = "true" autoDeploy = "true">
<!--
docBase : Web应用目录或者war包的部署路径,可以是绝对路径也可以是相对于Host appBase的相对路径。
path:web应用的context路径。如果我们Host,名为localhost,则该web应用访问的根路径为:http://localhost:8080/web
-->
<Context docBase = "D://myapp" path="/web"/>
</Host>
文章作者: zenshin
文章链接: https://zlh.giserhub.com/2021/10/30/cl35o0mqx0039p4tgdtcbcvwz/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论