首发CSDN:徐同学呀,原创不易,转载请注明源链接。我是徐同学,用心输出高质量文章,希望对你有所帮助。
本篇基于Tomcat10.0.6。
文章目录
-
- 一、前言
- 二、Connector基本结构
- 三、ProtocolHandler
-
- 1、Endpoint通信端点
- 2、Processor应用层协议解析
-
- (1)ConnectionHandler创建合适的Processor
- (2)协议升级
- 3、配置方式
- 四、Adapter
- 五、请求处理完整流程
- 六、要点回顾
- 七、参考文献
一、前言
Tomcat是一个非常优秀的开源项目,深深被它匠心独具的架构理念所折服。技术的思维是互通的,哪有那么多的高深莫测,都是对现实世界的抽象。
Tomcat作为Web应用服务器,接收请求和处理请求是它的两个核心功能。接收请求这步需要考虑适配多种网络协议,主流的是
HTTP
协议,还有
WebSocket
协议、
AJP
协议等;处理请求就是找到对应的
Servlet
,执行业务代码。
为了解耦和扩展性,Tomcat 设计了两个核心组件来做这两件事情:
- 连接器(
)负责对外交流,建立Connector
连接,读取并解析网络字节流,生成Socket
和Request
对象并转发给容器。Response
- 容器(
)负责内部处理,加载和管理Container
,通过Servlet
对象的输出流写入响应结果。Response
二、Connector基本结构
具体来看看
Connector
有哪些功能:
- 监听网络端口。
- 接受网络连接请求。
- 读取网络请求字节流。
- 根据具体应用层协议(
)解析字节流,生成统一的HTTP/AJP
和Request
对象。(需要注意,Response
为了兼容多种协议,没有耦合Connector
标准,所以这里生成的Servlet
和Request
对象没有实现Response
和HttpServletRequest
接口)。HttpServletResponse
- 将
和Request
对象转换为Response
和HttpServletRequest
对象。HttpServletResponse
- 将
和HttpServletRequest
对象转发给HttpServletResponse
。Container
总结就是做了三件事:
- 网络通信。
- 应用层协议解析和封装。
-
和Request
对象转换和传递。Response
对应
Connector
中的三个组件就是:
Endpoint
、
Processor
、
Adapter
,其中
Endpoint
和
Processor
放在一起抽象成了
ProtocolHandler
协议组件。
三、ProtocolHandler
ProtocolHandler
负责传输层网络连接和应用层协议解析,由两个核心部件
Endpoint
和
Processor
具体做这两件事。
从实现类来看,Tomcat的连接器主要支持两种协议:
HTTP/1.1
协议和
AJP
协议,实则还支持
HTTP/2.0
协议,
HTTP/2.0
的处理与
HTTP/1.1
和
AJP
不同,采用一种升级协议的方式实现。
-
协议:大家最熟悉的协议了,绝大多数web应用采用的访问协议,Tomcat既是HTTP/1.1
容器又是HTTP服务器,单独运行就可以对外提供服务,但是一般会搭配Servlet
web服务器做反向代理和负载均衡。Nginx
-
协议:自Tomcat8.5开始支持,相较于HTTP/2.0
采用二进制传输数据而非文本格式;对消息头采用HTTP/1.1
压缩,提升传输效率;基于帧和流的多路复用,真正实现了基于一个连接多请求并发处理;支持服务器主动推送。HPACK
-
协议:全名Apache JServ Protocol,是Alexei Kosut创建的定向包通信协议,采用二进制格式传输可读文本。用于集成Web服务器,以提升静态资源的访问性能,当前最新版本为1.3。目前AJP/1.3
、Apache
、Tomcat
、Nginx
、Jetty
等均已支持JBoss
。(名不见经传,不过多解释)AJP
除了支持3种协议外,还分别支持3种I/O方式:
NIO
、
NIO2
、
APR
,Tomcat8.5之前默认还支持
BIO
,后来因为性能问题直接给删除了,APR也贴上了过期标签。协议和I/O方式两两组合就出现了很多实现类:
Http11NioProtocol
、
Http11Nio2Protocol
、
Http11AprProtocol
(已过期)、
AjpNioProtocol
、
AjpNio2Protocol
、
AjpAjpProtocol
(已过期)。(
HTTP/2.0
是在
HTTP/1.1
的基础上升级处理的,不在该继承体系内,
APR
采用
Apache
可移植运行库(
c++
编写的本地库,就是
native
方法)实现的,官方已标注过期不建议使用,所以不过多解释)
Tomcat采用
UpgradeProtocol
表示HTTP升级协议,当前只有一个实现类
Http2Protocol
用于处理
HTTP/2.0
。它根据请求创建一个用于升级处理的令牌
UpgradeToken
,该令牌包含了具体的HTTP升级处理器
HttpUpgradeHandler
(
HTTP/2.0
的处理器为
Http2UpgradeHandler
)。(Tomcat中
WebSocket
也是通过
UpgradeToken
机制实现的,其处理器为
WsHttpUpgradeHandler
)
1、Endpoint通信端点
Endpoint
负责网络通信,监听一个端口,循环接收
socket
请求,读取网络字节流等。
Endpoint
不是接口,而是提供了一个抽象类
AbstractEndpoint
,又根据I/O方式提供了若干实现类:
网络通信这一层是非常抽象的,传输层协议毋庸置疑是
TCP/IP
,但是如何监听socket请求,读取网络字节流的方式是多样的,同步非阻塞、I/O多路复用、异步非阻塞等等:
-
高度抽象出一个Endpoint
来循环监听Acceptor
请求;socket
- Tomcat是支持高并发的,但是机器的性能是有限的,为了保证Web服务器不被高流量冲垮,所以在接收请求前会有一个
限流器(利用LimitLatch
实现);AQS
- 接收到的
通道(socket
orSocketChannel
),会先根据I/O方式包装一下,如同步非阻塞AsynchronousSocketChannel
、异步非阻塞NioChannel
等;Nio2Channel
- 包装之后的
通道又用一个更抽象的socket
封装,以应对不同方式的网络字节流的读取和写入;SocketWrapper
- 出于高并发的设计,将抽象的
交由SocketWrapper
任务对象处理,SocketProcessor
会扔进一个线程池SocketProcessor
处理;Executor
- 为了进一步提高性能,每次处理请求都会把一些对象缓存起来,不重复创建,比如
、SocketWrapper
等。SocketProcessor
2、Processor应用层协议解析
Acceptor
接收到请求封装成一个
SocketProcessor
扔进线程池
Executor
后,会调用
Processor
从操作系统底层读取、过滤字节流,对应用层协议(
HTTP/AJP
)进行解析封装,生成
org.apache.coyote.Request
和
org.apache.coyote.Response
对象。不同的协议有不同的
Processor
,
HTTP/1.1
对应
Http11Processor
,
AJP
对应
AjpProcessor
,
HTTP/1.2
对应
StreamProcessor
,
UpgradeProcessorInternal
和
UpgradeProcessorExternal
用于协议升级:
(1)ConnectionHandler创建合适的Processor
SocketProcessor
并不是直接调用的
Processor
,而是通过
org.apache.coyote.AbstractProtocol.ConnectionHandler#process
找到一个合适的
Processor
进行请求处理:
- 根据不同协议创建
orHttp11Processor
;AjpProcessor
- 根据协议升级是内部升级(
)还是外部升级创建HTTP/2.0
orUpgradeProcessorInternal
。UpgradeProcessorExternal
(2)协议升级
如果是正常的协议,如
HTTP/1.1
、
AJP/1.3
,则
Processor#process
处理完请求后会直接调用
Adapter#service
,将请求转发给
Container
。
如果是协议升级(除
Websocket
),首先通过HTTP/1.1进行协议升级:
- 服务器接收到带有特殊请求头(
)的Upgrade
连接,因此仍会先交给HTPP/1.1
进行处理;Http11Processor
- 根据请求头
对应协议名创建Upgrade
,并赋值给当前UpgradeToken
;Processor
- 返回
,再由SocketState.UPGRADING
进行协议升级;ConnectionHandler
-
会从当前ConnectionHandler
获取Processor
对象(如果没有,则默认为HTTP/2.0),并构建一个升级的UpgradeToken
(若为Tomcat可以处理的协议升级(Processer
、HTTP/2.0
) ,则是WebSocket
,否则为UpgradeProcessorInternal
)。UpgradeProcessorExternal
- 替换当前
,并将当前Processer
释放回收;Processer
- 将
设置给UpgradeProcessor
中的UpgradeToken
,并调用HttpUpgradeHandler
进行初始化,开启升级协议的处理。HttpUpgradeHandler.init
- 由于
是多路复用协议,一个连接可以处理多个HTTP请求,所以对于HTTP/2.0
,会将每次请求响应交于Http2UpgradeHandler
处理,再由StreamProcessor
将请求提交给StreamProcessor
。Container
注意:
-
和UpgradeProcessorInternal
都实现了接口UpgradeProcessorExternal
,表示一个用于升级的连接,并不处理协议升级后数据读写和解析,而是交由WebConnection
,对于HttpUpgradeHandler
再构建Http2UpgradeHandler
,又将StreamProcessor
包装成任务类StreamProcessor
,扔进线程池StreamRunnable
处理。Executor
-
也属于协议升级,和WebSocket
升级方案一致,但是协议升级的判断机制有所不同,HTTP/2.0
升级判断不在连接器里,而是交由WebSocket
容器通过当前请求的过滤器Servlet
判断,如果是WsFilter
协议升级,则调用当前WebSocket
构建org.apache.catalina.connector.Request#upgrade
并传递给UpgradeToken
处理(调用钩子函数Http11Processor
),之后到了org.apache.coyote.AbstractProcessor#action
逻辑就跟ConnectionHandler
升级差不多了。HTTP/2.0
3、配置方式
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
executor="tomcatThreadPool"
redirectPort="8443">
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol"/>
</Connector>
-
是port
监听的端口。Connector
-
是应用层协议名,可填参数有protocol
、HTTP/1.1
、org.apache.coyote.http11.Http11NioProtocol
、AJP/1.3
,如果org.apache.coyote.ajp.AjpNioProtocol
不填,则默认为protocol
。Http11NioProtocol
-
表示connectionTimeout
接收到连接后等待超时时间,单位毫秒,默认20秒。Connector
-
表示使用一个共享线程池,若使用私有线程池,则executor
不需要指定,私有线程池可选参数有executor
、minSpareThreads=“10”
等maxThreads=“200”
-
表示非redirectPort
重定向到SSL
端口,当请求是SSL
请求,但是接收到的请求内容需要non-SSL
传输,则重定向到SSL
端口。SSL
- 若为
开启HTTP
支持,需要配置HTTP/2.0
。UpgradeProtocol
四、Adapter
Adapter
接口只有一个实现类
org.apache.catalina.connector.CoyoteAdapter
,其主要职责如下:
- 将
和org.apache.coyote.Request
转为实现了标准org.apache.coyote.Response
的Servlet
和org.apache.catalina.connector.Request
。org.apache.catalina.connector.Response
- 将请求体的
、serverName
、URI
传给version
组件做映射,匹配到合适的Mapper
、Host
、Context
。Wrapper
- 将
和Request
传给Response
处理,Container
通过管道Engine
传给Pipeline
,Host
再传给Host
,Context
再传给Context
,Wrapper
是最终的Wrapper
。Servlet
五、请求处理完整流程
六、要点回顾
任重而道远啊,本篇主要从
Connector
的整体架构进行讲解,主要弄明白
Connector
是如何设计的?为什么要这样设计?
Connector
作为独立模块,封装底层网络通信,使
Container
和具体的协议及I/O方式解耦,易扩展、高性能。这也使得架构变得抽象复杂,
Connector
需要应对多种协议和I/O方式的组合,高度的抽象和封装。
Connector
只负责接收和解析请求,具体的业务处理还需要交给
Container
,所以需要一个适配器作为
Connector
和
Container
连接的桥梁。
很多实现细节并没有展开,比如:
-
的工作原理没有展开,如何监听请求?如何限流?如何封装接收到的Endpoint
?如何将连接扔进线程池里处理?socket
-
应用层协议解析原理没有展开,如何读取底层字节流?如何解析封装应用层协议?如何封装生成Processor
和Request
?Response
- 适配器
如何做转发?如何找到对应的Adapter
、Host
、Context
。Wrapper
涉及的知识点很多,比如:
- 对传输层协议
的理解。TCP/IP
- 对I/O模型的理解(I/O多路复用、同步非阻塞、异步非阻塞等)。
- 对传输层协议的理解(HTTP协议真的要掌握,不然真看不懂代码是怎么解析请求行、请求头、请求体的)。
- 高并发架构设计(缓存、
响应式、传输层协议延迟加载)。Reactor
后续会一一讲解。
七、参考文献
- 书籍:《Tomcat架构解析》刘光瑞(Tomcat8.5)
- 书籍:《Tomcat内核设计剖析》汪建(Tomcat7)
- 极客时间:《深入拆解Tomcat & Jetty》李号双(Tomcat9.x)
- Tomcat源码:https://gitee.com/stefanpy/tomcat-source-code-learning (Tomcat10.0.6)
如若文章有错误理解,欢迎批评指正,同时非常期待你的留言和点赞。如果觉得有用,不妨点个在看,让更多人受益。