深入理解Tomcat(十)Connector
前言
终于进行到Connector
的分析阶段了,这也是Tomcat里面最复杂的一块功能了。Connector
中文名为连接器
,既然是连接器,它肯定会连接某些东西,连接些什么呢?connector组件的processor对象处理的request和response,connector有三种request和response对象:coyoteRequest和coyoteResponse(是专门用于Tomcat内部表示,和servlet无关),Catalina(实现了servlet规范,将上面的coyote的那对request和response适配成Catalina包下的request和response),facade(作用是做Catalina包下面的那对request和response的门面,也部分实现servlet的规范,还有其他public方法,但是不暴露出去)
Connector
用于接受请求并将请求封装成Request和Response,然后交给Container
进行处理,Container
处理完之后再交给Connector
返回给客户端。
要理解Connector
,我们需要问自己4个问题。
- (1)
Connector
如何接受请求的? - (2)如何将请求封装成Request和Response的?
- (3)封装完之后的Request和Response如何交给
Container
进行处理的? - (4)
Container
处理完之后如何交给Connector
并返回给客户端的?
先来一张Connector
的整体结构图
![](http://upload-images.jianshu.io/upload_images/845143-3b14ae6c1d58ebbf.png?imageMogr2/auto-orient/strip|imageView2/2/w/791/format/webp)
【注意】:不同的协议、不同的通信方式,ProtocolHandler
会有不同的实现。在Tomcat8.5中,ProtocolHandler
的类继承层级如下图所示。
![](http://upload-images.jianshu.io/upload_images/845143-8e10276901d6b803.png?imageMogr2/auto-orient/strip|imageView2/2/w/608/format/webp)
针对上述的类继承层级图,我们做如下说明:
- ajp和http11是两种不同的协议
- nio、nio2和apr是不同的通信方式
- 协议和通信方式可以相互组合。
ProtocolHandler
包含三个部件:Endpoint
、Processor
、Adapter
。
Endpoint
用来处理底层Socket的网络连接,Processor
用于将Endpoint
接收到的Socket封装成Request,Adapter
用于将Request交给Container进行具体的处理。Endpoint
由于是处理底层的Socket网络连接,因此Endpoint
是用来实现TCP/IP协议
的,而Processor
用来实现HTTP协议
的,Adapter
将请求适配到Servlet容器进行具体的处理。Endpoint
的抽象实现类AbstractEndpoint里面定义了Acceptor
和AsyncTimeout
两个内部类和一个Handler接口
。Acceptor
用于监听请求,AsyncTimeout
用于检查异步Request的超时,Handler
用于处理接收到的Socket,在内部调用Processor
进行处理。
至此,我们已经明白了问题(1)、(2)和(3)。至于(4),当我们了解了Container自然就明白了,前面章节内容已经详细分析过了。
Connector源码分析入口
我们在Service
标准实现StandardService
的源码中发现,其init()
、start()
、stop()
和destroy()
方法分别会对Connectors的同名方法进行调用。而一个Service
对应着多个Connector
。限于篇幅,本章不再罗列这部分代码,需要读者自行阅读tomcat源码。
【注】:本章我们仅对
http1.1协议且nio通信方式
的相关代码进行分析。
Connector启动逻辑
我们知道Connector
实现了Lifecycle
接口,所以它是一个生命周期组件
。所以Connector
的启动逻辑入口在于init()
和start()
。
Connector构造方法
在分析之前,我们看看server.xml
,该文件已经体现出了tomcat中各个组件的大体结构。
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log"