天天看点

深入理解Tomcat系列之三:Connector

前言

connector是tomcat的连接器,其主要任务是负责处理浏览器发送过来的请求,并创建一个request和response的对象用于和浏览器交换数据,然后产生一个线程用于处理请求,connector会把request和response对象传递给该线程,该线程的具体的处理过程是container容器的事了。执行过程分为以下几个步骤

实例化connector,构造一个connector对象

调用connector的initintenal方法,初始化connetor

调用protocolhanlder的init方法,完成protocolhanlder的初始化。这个过程包括了创建线程池并创建一个线程处理浏览器请求

调用connector的startintenal方法,启动connector

调用protocolhandler的start方法,启动protocolhanlder

调用mapperlistener的start方法,启动监听器程序

为了对connector的执行过程有一个大概的印象,可以参考下面的序列图:

深入理解Tomcat系列之三:Connector

注意:由于tomcat还支持ajp协议,但为了简化,我画的这个序列图是基于http协议的,这也是我们在web开发中接触最多的协议了。

在深入connector之前我们先看看connector类的结构:

深入理解Tomcat系列之三:Connector
深入理解Tomcat系列之三:Connector

既然是处理浏览器请求,那么需要支持http协议,在tomcat中有两种协议处理器:http/1.1与ajp/1.3协议处理器。在server.xml中已经指明tomcat所支持的两种协议:

代码清单3-1:

在tomcat中是怎么样分别处理这两种协议的呢,我们可以protocolhanlder类中找到答案:

深入理解Tomcat系列之三:Connector

图中被选中的就是tomcat默认使用协议处理器,其实现过程与java标准socket编程是一样的,在tomcat中可以使用connetor类的setprotocol方法,看看源码就知道了:

代码清单3-2:

从第2个if子句的最后一个else可以知道tomcat默认使用的是http1.1协议。

我们再看看connector的初始化过程:

代码清单3-3:

从这段代码中可以看到:首先调用父类org.apache.catalina.util.lifecyclembeanbase的初始化方法,然后创建一个adapter,然后设置protocolhanlder(协议处理器)的adapter,同时判断传过来的请求的请求方法(比如get或者post),如果没有指明请求方法,默认使用post处理,然后调用protocolhanlder的初始化方法,最后调用mapperlistener的初始化方法,而mapperlistener的初始化方法调用的是org.apache.catalina.util.lifecyclebase的init方法,我们重点关注protocolhanlder的初始化方法,具体是实现在abstractprotocol抽象类中,其直接子类有abstractajpprotocol和abstracthttp11protocol,分别对应的是两种不同的处理协议,所以协议处理器的初始化方法是在其子抽象类(实现protocolhanlder接口的抽象类)来实现的,这里看看abstracthttp11protocol的初始化方法:

代码清单3-4:

打断点调试可以知道oname的值是<code>tomcat:type=protocolhandler,port=auto-1,address="127.0.0.1"</code>,tponame是<code>tomcat:type=protocolhandler,port=auto-1,address="127.0.0.1"</code>,roname是<code>tomcat:type=globalrequestprocessor,name="http-bio-127.0.0.1-auto-1"</code>,我们重点关注endpoint的init方法,主要完成以下几个过程:

设置线程接收数和最大连接数

创建线程池,启动监听的线程监听用户请求

启动一个线程处理请求

初始化完成connector就可以启动了,启动阶段调用startinternal方法:

代码清单3-5:

可以看出connector调用<code>protocolhandler.start()</code>方法,继续看看这个方法的源码:

代码清单3-6:

这个方法又调用了<code>endpoint.start()</code>方法:

代码清单3-7:

然后又调用了org.apache.tomcat.util.net.abstractendpoint.startinternal()方法:

代码清单3-8:

启动一个线程处理异步请求

这里启动了一个异步线程处理请求,这个异步线程是如何执行的呢?

代码清单3-9:

org.apache.tomcat.util.net.jioendpoint.socketprocessor的职责是把具体的请求处理过程委派给org.apache.tomcat.util.net.jioendpoint.handler,然后根据handler返回的不同socketstate,来决定是否关闭连接或者进行下一轮处理。

代码清单3-10:

其中的process方法主要完成对request的解析,包括请求头、请求行和请求体

代码清单3-11:

首先在http11processor的process方法里,会先从socket里读取http请求数据,并解析请求头,构造request对象和response对象,然后调用adapter.service()方法。adapter.service()完成请求行以及请求体的解析,并把解析出来的信息封装到request和response对象中,adapter(确切说是org.apache.catalina.connector.coyoteadapter)是connector和container的桥梁,经过这一步,请求就从connector传递到container里了,adapter.service()方法之后便将封装了request以及response对象的socket传给container容器了。

要注意的是:最先处理请求的request是org.apache.coyote.request类型,这是一个tomcat中一个轻量级对象,完成基本的请求处理后很容易被jvm回收,那为什么不直接交给connector.request对象处理呢?由于后者是servlet容器真正传递的对象其完成的职责比前者复杂,这里使用org.apache.coyote.request主要减轻后者的任务负担,出于性能考虑才这么设计。

具体service方法清单如下:

代码清单3-12:

从<code>connector.getservice().getcontainer().getpipeline().getfirst().invoke(request, response);</code>这句代码中可以知道下一步的处理需要交给container容器了。

经过上面一系列复杂的操作流程,tomcat的connector已经完成了protocol.start()方法,返回connector的startintenal方法,还有一个步骤要完成就是<code>mapperlistener.start()</code>的方法了,整个执行过程比较简单,有两步:

执行connector的startintenal方法

执行mapperlistener的startintenal方法

代码清单3-14:

首先注册已初始化的组件,然后为这些组件添加监听器,最后添加容器之间的映射关系。这样经过上面两个大步骤以及n个小步骤,我们的connector才算启动完毕,可谓是路途艰辛啊!