Tomcat架构解析之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与Catalina的交互利用通过如下图所示:
在这里插入图片描述
    在Coyote中,Tomcat支持一下3中传输协议:

  • 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可移植运行库)实现。APR是使用C/C++编写的本地库,如果选择该方案,需要单独安装APR库。

    可以采用一种简单的分层视图来描述Tomcat对协议以及 I/O方式的支持,如下图所示:
在这里插入图片描述
    在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的传输方案决定的。

二、Web请求处理主要概念

    在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的请求处理的详细过程:
(1)当Connector启动时,会同时启动其持有的Endpoint实例。Endpoint并允许多个线程(有属性acceptorThreadCount确定),每个线程允许一个AbstractEndpoint.Acceptor实例。在AbstractEndpoint.Acceptor实例中监听端口通信(I/O方式不同,具体的处理方式也不同),而且只要Endpoint处于运行状态,始终循环监听。

(2)当监听到请求时,Acceptor将Socker封装为SocketWrapper实例(此时并未读取数据),并交由一个SocketProcessor对象处理(此过程也由线程池异步处理)。此部分根据I/O方式的不同处理会有所不同,如NIO采用轮询的方式检测SelectionKey是否就绪。如果就绪,则获取一个有效的SocketProcessor对象并且提交线程池处理。

(3)SocketProcessor是一个线程池Worker实例,每一个I/O方式均有自己的实现。他首先判断Socket的状态(如完成SSL握手),然后提交到ConnectionHandler处理。

(4)ConnectionHandler是AbstractProtocol的一个内部类,主要用于链接选择一个合适的Processor实现以进行请求处理。

  • 巍峨提升性能,他针对每个有效的理解都会缓存器Processor对象。不仅如此,当前链接关闭时,其Processor对象还会被释放到一个回收队列(升级协议不会回收),这样后续链接可以重置并且重复利用,以减少对象构造。
  • 因此,在处理请求时,他首先会从缓存中获取当前链接的Processor对象。如果不存在,则尝试根据协商协议构造Processor(如HTTP/2.0请求)。如果不存在协商协议(如HTTP/1.1请求),则从回收队列中获取一个已释放的Processor对象使用。如果回收队列中没有可用的对象,那么由具体的协议创建一个Processor使用(同时注册到缓存)。

(5)协议升级时,ConnectionHandler会从当前Processor得到一个UpgradeToken对象(如果没有,则默认为HTTP/2),并构造一个升级Processor实例(如果是Tomcat支持的协议则会是UpgradeProcessorInternal,否则是UpgradeProcessorExternal)替换当前的Processor,并将当前的Processor释放回收。替换后,该链接的后续处理将由升级Process完成。

(6)通过UpgradeToken中HttpUpgradehandler对象的init()方法进行初始化,以便准备开始启用新协议。

四、协议升级

    在8.5版本,Tomcat重构了协议升级的实现方案,以支持HTTP/2.0,而且WebSocket也该由新的升级方案实现。尽管HTTP/2.0与WebSocket底层的升级方案是一致的,但是他们对协议协商的片段机制却是不同的,具体如下所示:
在这里插入图片描述
    在Servlet规范3.1中,首先通过WebConnection接口表示一个用于升级请求的链接。TOmcat中的UpgradeProcessorExternal和UpgradeProcessorInternal类均实现了该接口。其次,又通过HttpUpgradeHandler接口表示升级协议的处理过程,Tomcat目前只提供了HTTP/2.0和WebSocket两种协议的支持。

    对于升级协议,Tomcat通过一个UpgradeToken对象维护与其相关的信息,如当前Web应用上下文(StandardContexxt)、对象实例管理器(用于实例化对象)以及当前使用的HttpUpgradeHandler实例。无论HTTP/2.0还是WebSocket,均是先构造一个UpgradeTokend对象,然后根据他创建UpgradeProcessorInternal实例,并替换当前的Http11Processor以完成协议升级。当前链接的后续处理均由UpgradeProcessorinternal维护的HttpUpgradeHandler完成。

    HTTP/2.0通过8.5新增的UpgradeProtocol接口创建HttpUpgradeHandler以及UpgradeToken,而WebSocket则是通过过滤器WsFilter片段当前请求是否为WebSocket升级请求,如果是,则调用当前请求的upgrade()方法构造UpgradeToken并传递给Http11Processor。

    也就是说,对于第一次协议协商的过程,HTTP/2.0是由链接器直接处理的,并未提交到Servlet容器,而WebSocket则提交到了Servlet容器。

    由于HTTP/2.0是多路复用的协议,也就是多个HTTP请求通过一个链接完成。因此对于Http2UpgradeHandler,会将每次请求响应交给StreamProcessor处理。而StreamProcessor则会将请求提交到Servlet容器。

发布了185 篇原创文章 · 获赞 457 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_42146366/article/details/98043043
今日推荐