Tomcat 最底层应用的是 Java 规范的 SocketServer 和 Socket 承受和解决申请,然而 Socket 承受到的数据是网络运输层的 TCP 或 UDP 协定的数据,须要转为 Http 或者其它应用层协定的数据。Tomcat 中就是通过连接器 Connector 来治理 Socket 连贯、解析 Scoket 申请为 Request 并封装响应数据为 Response 对象。新版本的 Tomcat 容器应用的连接器是 Coyote 框架,本文会具体介绍 Connector 的 Coyote 框架原理。本文很多内容参考了这篇博客
什么是 Coyote
Coyote 是 Tomcat 链接器框架的名称,是 Tomcat 服务器提供的供客户端拜访的内部接口。客户端通过 Coyote 与服务器建设链接、发送申请并且接管响应。
Coyote 封装了底层的网络通信(Socket 申请以及响应解决),为 Catalina 容器提供了对立的接口,是 Catalina 容器与具体的申请协定以及 I/ O 形式解耦。Coyote 将 Socket 输出转换为 Request 对象,交由 Catalina 容器进行解决,解决申请实现之后,Catalina 通过 Coyote 提供的 Response 对象将后果写入输入流。
Coyote 作为独立的模块,只负责具体协定和 I / O 的解决,与 Servlet 标准实现没有间接关系,因而即使是 Request 和 Response 对象也并未实现 Servlet 标准对应的接口,而是在 Catalina 中将他们进一步封装为 ServletRequest 何 ServletResponse。
Coyote 反对的协定
Coyote 框架反对以下三种应用层协定:
- HTTP/1.1 协定:这是绝大多数 Web 利用采纳的拜访协定,次要用于 Tomcat 独自运行(不予 Web 服务器集成)的状况。
- AJP 协定:用于和 Web 服务器(如 Apache HTTP Server)集成,以实现针对动态资源的优化以及集群部署,以后反对 AJP/1.3。
- HTTP/2.0 协定:下一代 HTTP 协定,自 Tomcat8.5 以及 9.0 版本开始反对,截止目前,支流的最新版本均已反对 HTTP/2.0。
针对 HTTP 和 AJP 协定,Coyote 又依照 I/ O 形式别离提供了不同的抉择计划(自 8.5/9.0 版本起,Tomcat 已出了对 BIO 的反对)。
- NIO:采纳 Java NIO 类库实现。
- NIO2:采纳 JDK 7 最新的 NIO2 类库实现。
- APR:采纳 APR(Apache 可移植运行库)实现
在 8.0 之前,Tomcat 默认采纳的 I / O 形式为 BIO,之后采纳 NIO。无论 NIO、NIO2 还是 APR,在性能方面均优于以往的 BIO。如果采纳 APR,甚至能够达到靠近于 Apache HTTP Server 的响应性能。
在 Coyote 中,HTTP/2.0 的解决形式与 HTTP/1.1 和 AJP 不同,采纳一种降级协定的形式实现,这也是有 HTTP/2.0 的传输计划决定的。
能够采纳一种简略的分层视图来形容 Tomcat 对协定以及 I/ O 形式的反对,如下图所示:
Connector 的外围概念
在 Connector 中有如下几个外围概念:
- Endpoint:Coyote 通信端点,即通信监听的接口,是具体的 Socker 接管解决类,是对传输层的形象。Tomcat 并没有 Endpoint 接口,而是提供了一个抽象类 AvstractEndpoint。依据 I / O 形式的不同,提供了 NioEndpoint(NIO)、AprEndpoint(APR)以及 Nio2Endpoint(NIO2)三个实现。
- Processor:Coyote 协定解决接口,负责结构 Request 和 Response 对象,并且通过 Adapter 将其提交到 Catalina 容器解决,是对应用层的形象。Processor 是单线程的,Tomcat 在同一次链接中复用 Processor。Tomcat 依照协定的不同提供了 3 个实现类:Http11Processor(HTTP/1.1)、AjpProcessor(AJP)、StreamProcessor(HTTP/2.0)。除此之外,他还提供了两个用于解决外部解决的实现:UpgradeProcessorInternal 和 UpgradeProcessorExternal,前者用于解决外部反对的降级协定(如 HTTP/2.0 和 WebSocket),后者用于解决内部扩大的降级协定反对。
- UpgradeProtocol:Tomcat 采纳 UpgradeProtocol 接口示意 HTTP 降级协定,以后只提供了一个实现(Http2Protocol)用于解决 HTTP/2.0. 他依据申请创立一个用于降级解决的令牌 UpgradeToken,该令牌中蕴含了具体的 HTTP 降级处理器 HttpUpgradeHandler,HTTP/2.0 的处理器实现为 Http2UpgradeHandler。Tomcat 中的 WebSocket 也是通过 UpgradeToken 机制实现的。
Connector 申请解决流程
Connector 解决申请的流程如上所示,咱们一步步剖析一下流程的内容:
- 当 Connector 启动时,会同时启动其持有的 Endpoint 实例。Endpoint 并容许多个线程(有属性 acceptorThreadCount 确定),每个线程容许一个 AbstractEndpoint.Acceptor 实例。在 AbstractEndpoint.Acceptor 实例中监听端口通信(I/ O 形式不同,具体的解决形式也不同),而且只有 Endpoint 处于运行状态,始终循环监听。
- 当监听到申请时,Acceptor 将 Socker 封装为 SocketWrapper 实例(此时并未读取数据),并交由一个 SocketProcessor 对象解决(此过程也由线程池异步解决)。此局部依据 I / O 形式的不同解决会有所不同,如 NIO 采纳轮询的形式检测 SelectionKey 是否就绪。如果就绪,则获取一个无效的 SocketProcessor 对象并且提交线程池解决。
- SocketProcessor 是一个线程池 Worker 实例,每一个 I / O 形式均有本人的实现。他首先判断 Socket 的状态(如实现 SSL 握手),而后提交到 ConnectionHandler 解决。
-
ConnectionHandler 是 AbstractProtocol 的一个外部类,次要用于链接抉择一个适合的 Processor 实现以进行申请解决。
- 晋升性能,他针对每个无效的了解都会缓存器 Processor 对象。不仅如此,以后链接敞开时,其 Processor 对象还会被开释到一个回收队列(降级协定不会回收),这样后续链接能够重置并且反复利用,以缩小对象结构。
- 在解决申请时,他首先会从缓存中获取以后链接的 Processor 对象。如果不存在,则尝试依据协商协定结构 Processor(如 HTTP/2.0 申请)。如果不存在协商协定(如 HTTP/1.1 申请),则从回收队列中获取一个已开释的 Processor 对象应用。如果回收队列中没有可用的对象,那么由具体的协定创立一个 Processor 应用(同时注册到缓存)。
- 协定降级时,ConnectionHandler 会从以后 Processor 失去一个 UpgradeToken 对象(如果没有,则默认为 HTTP/2),并结构一个降级 Processor 实例(如果是 Tomcat 反对的协定则会是 UpgradeProcessorInternal,否则是 UpgradeProcessorExternal)替换以后的 Processor,并将以后的 Processor 开释回收。替换后,该链接的后续解决将由降级 Process 实现。
- 通过 UpgradeToken 中 HttpUpgradehandler 对象的 init() 办法进行初始化,以便筹备开始启用新协定。
我是御狐神,欢送大家关注我的微信公众号:wzm2zsd
本文最先公布至微信公众号,版权所有,禁止转载!