一、Tomcat组件介绍
1、简介
Tomcat是一个基于JAVA的WEB容器,其实现了JAVA EE中的 Servlet 与 jsp 规范,与Nginx(一般用于反向代理、负载均衡,屏蔽内部细节)、apache 服务器不同在于一般用于动态请求处理。在架构设计上采用面向组件的方式设计。即整体功能是通过组件的方式拼装完成。另外每个组件都可以被替换以保证灵活性。
那么是哪些组件组成了Tomcat呢?

2、Tomcat 各组件及关系
- Server
- Service
- Connector 连接器
- HTTP 1.1
- SSL https
- AJP( Apache JServ Protocol) apache 私有协议,用于apache 反向代理Tomcat
- Connector 连接器
- Container
- Engine 引擎 catalina
- Host 虚拟机 基于域名 分发请求
- Context 隔离各个WEB应用 每个Context的 ClassLoader独立
- Host 虚拟机 基于域名 分发请求
- Engine 引擎 catalina
- Service
- Component
- Manager (管理器)
- logger (日志管理)
- loader (载入器)
- pipeline (管道)
- valve (管道中的阀)

3、Tomcat server.xml 配置
**server **
root元素——server 的顶级配置
主要属性:
- port:执行关闭命令的端口号
- shutdown:关闭命令
1 | |
service
服务:将多个connector 与一个Engine组合成一个服务,可以配置多个服务。
Connector
连接器:用于接收指定协议下的连接 并指定给唯一的Engine 进行处理。
主要属性:
- protocol 监听的协议,默认是http/1.1(可以指定特性的)
- port 指定服务器端要创建的端口号
- minThread 服务器启动时创建的处理请求的线程数
- maxThread 最大可以创建的处理请求的线程数
- enableLookups 如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址
- redirectPort 指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号
- acceptCount 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理
- connectionTimeout 指定超时的时间数(以毫秒为单位)
- SSLEnabled 是否开启 sll 验证,在Https 访问时需要开启。
1 | |
Engine
引擎:用于处理连接的执行器,默认的引擎是catalina。一个service 中只能配置一个Engine。
主要属性:
- name 引擎名称
- defaultHost 默认host
Host
虚拟机:基于域名匹配至指定虚拟机。类似于nginx 当中的server,默认的虚拟机是localhost
主要属性:
1 | |
Context
应用上下文:一个host 下可以配置多个Context ,每个Context 都有其独立的classPath。相互隔离,以免造成ClassPath 冲突。
主要属性:
1 | |
Valve
阀门:可以理解成过滤器,具体配置要基于具体的Valve 接口的子类。以下即为一个访问日志的Valve.
1 | |
4、Tomcat 自动部署脚本编写
Tomcat启动参数说明
我们平时启动Tomcat过程是怎么样的?
- 复制WAR包至Tomcat webapp 目录。
- 执行starut.bat 脚本启动。
- 启动过程中war 包会被自动解压装载。
但是我们在Eclipse 或idea 中启动WEB项目的时候 也是把War包复杂至webapps 目录解压吗?显然不是,其真正做法是在Tomcat程序文件之外创建了一个部署目录,在一般生产环境中也是这么做的 即:Tomcat 程序目录和部署目录分开 。
我们只需要在启动时指定CATALINA_HOME 与 CATALINA_BASE 参数即可实现。
| 启动参数 | 描述说明 |
|---|---|
| JAVA_OPTS | jvm 启动参数 , 设置内存 编码等 -Xms100m -Xmx200m -Dfile.encoding=UTF-8 |
| JAVA_HOME | 指定jdk 目录,如果未设置从java 环境变量当中去找。 |
| CATALINA_HOME | Tomcat 程序根目录 |
| CATALINA_BASE | 应用部署目录,默认为$CATALINA_HOME |
| CATALINA_OUT | 应用日志输出目录:默认$CATALINA_BASE/log |
| CATALINA_TMPDIR | 应用临时目录:默认:$CATALINA_BASE/temp |
可以编写一个脚本 来实现自定义配置:
更新 启动 脚本
1 | |
自动部署脚本:
1 | |
二、Tomcat通信模型原理与源码
2.1、tomcat支持的4种IO模型
1、什么是IO?
IO是指为数据传输所提供的输入输出流,其输入输出对象可以是:文件、网络服务、内存等。
2、什么是IO模型
通常情况下IO操作是比较耗时的,所以为了高效的使用硬件,应用程序可以用一个专门线程进行IO操作,而另外一个线程则利用CPU的空闲去做其它计算。这种为提高应用执行效率而采用的IO操作方法即为IO模型。
3、各IO简单说明
| IO模型 | 描述 |
|---|---|
| BIO | 同步阻塞式IO,即Tomcat使用传统的java.io进行操作。该模式下每个请求都会创建一个线程,对性能开销大,不适合高并发场景。优点是稳定,适合连接数目小且固定架构。 |
| NIO | 同步非阻塞式IO,jdk1.4 之后实现的新IO。该模式基于多路复用选择器监测连接状态在同步通知线程处理,从而达到非阻塞的目的。比传统BIO能更好的支持并发性能。Tomcat 8.0之后默认采用该模式。优点是适合连接数目大且连接时间较短的场景 |
| APR | 全称是 Apache Portable Runtime/Apache可移植运行库),是Apache HTTP服务器的支持库。可以简单地理解为,Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作。使用需要编译安装APR库 |
| AIO(asynchronous I/O) | 异步非阻塞式IO,jdk1.7后之支持 。与nio不同在于不需要多路复用选择器,而是请求处理线程执行完程进行回调调知,以继续执行后续操作。Tomcat 8之后支持。优点是适合连接数目大且连接时间较短的场景 |
4、Tomcat使用指定IO模型的配置方式
配置 server.xml 文件当中的
默认配置 8.0 protocol=“HTTP/1.1” 8.0 之前是 BIO 8.0 之后是NIO
BIO
protocol=“org.apache.coyote.http11.Http11Protocol“
NIO
protocol=”org.apache.coyote.http11.Http11NioProtocol“
AIO
protocol=”org.apache.coyote.http11.Http11Nio2Protocol“
APR
protocol=”org.apache.coyote.http11.Http11AprProtocol“
2.2、Tomcat BIO、NIO实现过程源码解析
BIO(JioEndPoint)
3个组成成分都实现了runnable
Acceptor:接受所有连接,分配给SocketProcessor处理
SocketProcessor:接受连接,分配线程池线程

NIO(NioEndPoint)
3个组成成分都实现了runnable
Acceptor:接受所有连接,注册给Poller处理
Poller:监听、分配任务给SocketProcessor
SocketProcessor:接受连接,分配线程池线程

2.3、Tomcat connector 并发参数解读
| 名称 | 描述 |
|---|---|
| acceptCount | 等待最大队列 |
| address | 绑定客户端特定地址,127.0.0.1 |
| bufferSize | 每个请求的缓冲区大小。bufferSize * maxThreads |
| compression | 是否启用文档压缩 |
| compressableMimeTypes | text/html,text/xml,text/plain |
| connectionTimeout | 客户发起链接 到 服务端接收为止,中间最大的等待时间 |
| connectionUploadTimeout | upload 情况下连接超时时间 |
| disableUploadTimeout | true 则使用connectionTimeout |
| enableLookups | 禁用DNS查询 true |
| keepAliveTimeout | 当长链接闲置 指定时间主动关闭 链接 ,前提是客户端请求头 带上这个 head”connection” “ keep-alive” |
| maxKeepAliveRequests | 最大的 长连接数 |
| maxHttpHeaderSize | |
| maxSpareThreads | BIO 模式下 最多线闲置线程数 |
| maxThreads(执行线程) | 最大执行线程数 |
| minSpareThreads(初始线业务线程 10) | BIO 模式下 最小线闲置线程数 |
2.4、类加载
1、什么是类加载
类加载是加载Class文件进入JVM。它负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte,它有特定的复杂的内部格式。
2、JVM的类加载器
JVM 运行实例中会存在多个 ClassLoader,不同的 ClassLoader 会从不同的地方加载字节码文件。它可以从不同的文件目录加载,也可以从不同的 jar 文件中加载,也可以从网络上不同的静态文件服务器来下载字节码再加载。JVM中类加载器层次结构如下

1、启动类加载器:Bootstrap ClassLoader,用于加载JVM提供的基础运行类,即位于**%JAVA_HOME%/jre/lib目录下的核心类库**;
2、扩展类加载器:Extension ClassLoader, Java提供的一个标准的扩展机制用于加载除核心类库外的Jar包,即只要复制到指定的扩展目录(可以多个)下的Jar, JVM会自动加载(不需要通过-classpath指定)。默认的扩展目录是%JAVA_HOME%/jre/lib/ext。典型的应用场景就是,Java使用该类加载器加载JVM默认提供的但是不属于核心类库的Jar。不推荐将应用程序依赖的类库放置到扩展目录下,因为该目录下的类库对所有基于该JVM运行的应用程序可见;
3、应用程序类加载器:Application ClassLoader ,用于加载环境变量CLASSPATH (不推荐使用)指定目录下的或者-classpath运行参数指定的Jar包。System类加载器通常用于加载应用程序Jar包及其启动入口类(Tomcat 的Bootstrap类即由System类加载器加载)
1 | |
3、Tomcat 的类加载器
下图不表示类加载顺序,只代表结构

引导类加载器 和 扩展类加载器 的作⽤不变
系统类加载器正常情况下加载的是 CLASSPATH 下的类,但是 Tomcat 的启动脚本并未使⽤该变量,⽽是加载tomcat启动的类,⽐如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin下
Common 通⽤类加载器加载Tomcat使⽤以及应⽤通⽤的⼀些类,位于CATALINA_HOME/lib下,⽐如servlet-api.jar
Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问
Shared ClassLoader ⽤于加载应⽤程序共享类,这些类服务器不会依赖
Webapp ClassLoader,每个应⽤程序都会有⼀个独⼀⽆⼆的Webapp ClassLoader,他⽤来加载本应⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。
2.5、Tomcat如何双亲委派机制
1、什么是双亲委派机制
当某个类加载器需要加载某个.class⽂件时,它⾸先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,⾃⼰才会去加载这个类。JVM的类加载就是严格按照该机制进行的
2、双亲委派机制的作用
1、防⽌重复加载同⼀个.class。通过委托去向上⾯问⼀问,加载过了,就不⽤再加载⼀遍。保证数据安全。
2、保证核⼼.class不能被篡改。通过委托⽅式,不会去篡改核⼼.class,即使篡改也不会去加载,即使加载也不会是同⼀个.class对象了。这样保证了class执⾏安全(如果⼦类加载器先加载,那么我们可以写⼀些与java.lang包中基础类同名的类, 然后再定义⼀个⼦类加载器,这样整个应⽤使⽤的基础类就都变成我们⾃⼰定义的类了。)如
3、何打破双亲委派
WebappClassLoaderBase重写loadClass(),delegate默认为false,因此打破了双亲委派机制
可以通过配置
1 | |
4、Tomcat类加载顺序
默认情况下(打破双亲委派)
- JVM 的 Bootstrap 类
- Web 应用的 /WEB-INF/classes 类
- Web 应用的 /WEB-INF/lib/*.jar 类
- System 类加载器的类
- Common 类加载器的类
遵循双亲委托
- JVM 的 Bootstrap 类
- System 类加载器的类
- Common 类加载器的类
- Web 应用的 /WEB-INF/classes 类
- Web 应用的 /WEB-INF/lib/*.jar 类
4、为何打破后依旧先加载Bootstrap的类
jvm的一些基础类不允许重写,所以bootstrapClassLoader始终是最先加载的
5、不同的加载器加载同一个class的到的class对象也是不同的
三、Tomcat处理HTTP原理
1、请求的流程
HTTP请求 -> Ip + Port -> 操作系统 -> Tomcat(1) ->根据Http协议解析成Request-> Engine(1) -> Host(n) -> Context(n) -> Wrapper(n) ->doFilter -> service() -> doGet()/doPost()




2、Http请求字节流如何解析成HttpServletRequest
1、Http请求头格式


JIoEndPoint(InternalInputBuffer)就是按照Http协议请求头的格式去解析字节流,从而解析成HttpServletRequest对象(一般而言通过请求头的Content-Length表示请求体长度,或者分块传输——Transfer-Encoding为chunk)