【Tomcat】【五】Connector 分析
1 前言
Connector用于接收请求并将请求封装成 Request 和 Response 来具体处理,最底层是使用Socket 来进行连接的,Request 和 Response 是按照HTTP 协议来封装的,所以 Connector 同时实现了TCP/IP协议和HTTP 协议,Request和 Response 封装完之后交给 Container 进行处理Container 就是 Servlet 的容器,Container处理完之后返回给 Connector,最后 Connector 使用Socket将处理结果返回给客户端,这样整个请求就处理完了。
2 Connector的结构
Connector中具体是用 ProtocolHandler 来处理请求的,不同的 ProtocolHandler 代表不同的连接类型,比如,Httpl1Protocol使用的是普通 Socket 来连接的,Http11NioProtocol使用的是NioSocket 来连接的。
ProtocolHandler 里面有3个非常重要的组件:Endpoint、Processor和Adapter。Endpoint用于处理底层Socket的网络连接,Processor 用于将Endpoint 接收到的Socket封装成Request,Adapter用于将封装好的 Request交给 Container 进行具体处理。也就是说Endpoint用来实现TCP/IP协议,Processor 用来实现HTTP 协议,Adapter 将请求适配到 Servlet 容器进行具体处理。
Endpoint 的抽象实现AbstractEndpoint 里面定义的Acceptor 和AsyncTimeout 两个内部类和一个Handler 接口。Acceptor 用于监听请求,AsyncTimeout 用于检查异步 request 的超时Handler 用于处理接收到的 Socket,在内部调用了 Processor 进行处理。
3 Connector自身类
Connector类本身的作用主要是在其创建时创建 ProtocolHandler,然后在生命周期的相关方法中调用了 ProtocolHandler 的相关生命周期方法。Connector 的使用方法是通过 Connector标签配置在 conf/server.xml文件中,所以 Connector 是在 Catalina 的 load 方法中根据 conf/server.xml配置文件创建Server 对象时创建的。Connector 的生命周期方法是在 Service 中调用的。
3.1 Connector的创建
Connector 的创建过程主要是初始化 ProtocolHandler。serverxml配置文件中Connector标签的 protocol属性会设置到Connector构造函数的参数中,它用于指定 ProtocolHandler的类型,Connector的构造函数代码如下:
这里首先根据传人的 protocol参数调用 setProtocol方法设置了 protocolHandlerClassName 属性,接着用 protocolHandlerClassName 所代表的类创建了 ProtocolHandler 并赋值给了 protocol-Handler 属性。
设置protocolHandlerClassName 属性的 setProtocol方法代码如下:
Apr是Apache Portable Runtime 的缩写,是Apache提供的一个运行时环境,如果要使用Apr需要先安装,安装后Tomcat 可以自已检测出来。如果安装了Apr,setProtocol方法会根据配 置的HTTP/1.1属性对应地将 protocolHandlerClassName 设 置为 orgapache.coyote.htpl1.Httpl1AprProtocol,如果没有安装 Apr,会根据配置的HTTP/1.1属性将 protocolHandler-ClassName 设 置为 orgapache.coyote.httpl1.Htpl1NioProtocol,然后就会根 据 protocol-HandlerClassName来创建 ProtocolHandler。
3.2 Connector生命周期处理方法
Connector的生命周期处理方法中主要调用了 ProtocolHandler 的相应生命周期方法,代码如下:
在initInternal方法中首先新建了一个Adapter 并设置到 ProtocolHandler 中,然后对ProtocolHandler 进行初始化;在 startIntermal方法中首先判断设置的端口是否小于0,如果小于0就抛出异常,否则就调用 ProtocolHandler 的 start 方法来启动;在 stopnternal方法中先设置了生命周期状态,然后调用了 ProtocolHandler 的 stop 方法;在 destroyInternal方法中除了调用 ProtocolHandler 的 destroy 方法,还会将当前的 Connector 从 Service 中删除并调用父类的destroyInternal方法。
4 ProtocolHandler
Tomcat 中ProtocolHandler 的继承结构如图7-7所。ProtocolHandler有一个抽象实现类AbstractProtocolAbstractProtocol下面分了三种类型:Ajp、HTTP和Spdy。Ajp是ApacheJServ Protocol的缩写,Apache的定向包协议,主要用于与前端服务器(如Apache)进行通信,它是长连接,不需要每次通信都重新建立连接,这样就节省了开销;HTTP协议前面已经介绍过了,就不重复介绍了;Spdy 协议是 Google开发的协议,作用类似HTTP,比HTTP 效率高,不过这只是Google 制定的企业级协议,使用并不广泛,而且在HTTP/2 协议中已经包含了 Spdy 所提供的优势,所以Spdy 协议平常很少使用,不过 Tomcat 提供了支持。
这里的 ProtocolHandler 以默认配置中的 orgapache.coyote.http11.Httpl1NioProtocol 为例来分析,它使用HTTP11协议,TCP 层使用NioSocket 来传输数据。Http11NioProtocol的构造函数中创建了 NioEndpoint类型的Endpoint,并新建了Http11-ConnectionHandler类型的Handler 然后设置到了 Endpoint中,代码如下:
四个生命周期方法是在父类AbstractProtocol 中实现的,其中主要调用了 Endpoint 的生命周期方法。
5 处理TCP/IP协议的Endpoint
Endpoint用于处理具体连接和传输数据,NioEndpoint继承自orgapache.tomcat.utilnetAbstractEndpoint,在NioEndpoint 中新增了 Poller和 SocketProcessor 内部类,NioEndpoint中处理请求的具体流程如图7-8 所示。
NioEndpoint的init和start 方法在父类AbstractEndpoin 中,代码如下:
这两个方法主要调用bind和startInternal方法,它们是模板方法,在NioEndpoint中实现,bind方法代码如下:
这里的 bind方法中首先初始化了 ServerSocketChannel,然后检查了代表Acceptor 和Poller初始化的线程数量的 acceptorThreadCount 属性和 pollerThreadCount 属性,它们至少为1,Acceptor 用于接收请求,接收到请求后交给 Poller 处理,它们都是启动线程来处理的。另外还处理了初始化SSL等内容。
NioEndpoint的startInternal方法代码如下:
这里首先初始化了一些属性,然后启动了 Poller 和Acceptor 来处理请求,初始化的属性中的 processorCache属性是 SynchronizedStack<SocketProcessor>类型,SocketProcessor 并不是前面介绍的 Processor,而是 NioEndpoint 的一个内部类,Poller 接收到请求后就会交给它处理,SocketProcessor 又会将请求传递到 Handler。启动 Acceptor 的 startAcceptorThreads 方法在AbstractEndpoint中,代码如下:
这里的getAcceptorThreadCount方法就是获取的init方法中处理过的acceptorThreadCount属性,获取到后就会启动相应数量的 Acceptor 线程来接收请求。
6 处理HTTP协议的Processor
Processor 用于处理应用层协议(如HTTP),它的继承结构如图所示。
Processor有两个AbstractProtocol抽象继承类,图7-9中上面的AbstractProtocol是在orgapache.coyote.httpll.upgrade 包中,下面的AbstractProtocol在 orgapache.coyote 包中。正常处理协议使用的是下面的AbstractProtocol及其实现类,上面的AbstractProtocol是 Servlet3.1之后才新增的,用于处理HTTP的升级协议,当正常(下面)的 Processor 处理之后如果Socket 的状态是UPGRADING,那么Endpoint 中的Handler将会接着创建并调用orgapache.coyote.httpll.upgrade包中的 Processor 进行处理,这里的HTTP升级协议指的是WebSocket 协议。
图中下方orgapache.coyote包中的Processor 和前面介绍过的ProtocolHandler一一对应,这里就不详细解释了,具体实现应用层协议处理请求的是 AbstractAipProcessor 和Abst-ractHttp11Processor 中的 process方法,这个方法中首先封装了Request和 Response,然后调用Adapter将请求传递到了Container 中,最后对处理的结果进行了处理,如有没有启动异步处理、处理过程中有没有抛出异常等。
7 适配器Adapter
Adapter 只有一个实现类,那就是 org.apache.catalina.connector包下的 CoyoteAdapter类Processor在其 process 方法中会调用Adapter 的service方法来处理请求,Adapter 的 service方法主要是调用Container 管道中的invoke方法来处理请求,在处理之前对 Request和 Response做了处理,将原来创建的org.apache.coyote 包下的 Request 和 Response 封装成了org.apache.catalina.connector的 Request和 Response,并在处理完成后判断是否启动了 Comet(长连接推模式)和是否启动了异步请求,并作出相应处理。调用Container 管道的相应代码片段如下:
这里首先从Connector 中获取到 Service(Connector在initInternal方法中创建Coyote-Adapter 的时候已经将自已设置到了 CoyoteAdapter 中),然后从 Service 中获取 Container,接着获取管道,再获取管道的第一个 Value,最后调用invoke 方法执行请求。Service 中保存的是最顶层的容器,当调用最顶层容器管道的 invoke 方法时,管道将逐层调用各层容器的管道中Value 的invoke方法,直到最后调用 Wrapper 的管道中的 BaseValue (StandardWrapperValve)来处理Filter和Servlet。
8 小结
本文我们主要看了下 Connector的组成以及实现过程,有理解不对的地方欢迎指正哈。