前言
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还支持ajp协议,但为了简化,我画的这个序列图是基于http协议的,这也是我们在web开发中接触最多的协议了。
在深入connector之前我们先看看connector类的结构:
既然是处理浏览器请求,那么需要支持http协议,在tomcat中有两种协议处理器:http/1.1与ajp/1.3协议处理器。在server.xml中已经指明tomcat所支持的两种协议:
代码清单3-1:
在tomcat中是怎么样分别处理这两种协议的呢,我们可以protocolhanlder类中找到答案:
图中被选中的就是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才算启动完毕,可谓是路途艰辛啊!