整体架构
首先,认识一下tomcat核心组件:server
、service
、connector
、container/engine
- server:最顶层的容器,代表着整个服务器,掌管整个Tomcat的生死大权。一个 server 可以有多个 service
- service:一个 service 有多个connector和一个container
- connector:监听网络端口,接受网络请求
- container:接收connector发来的request请求,并进行业务处理
包含关系如下图所示:
上面所说的包含关系,都可以在Tomcat的conf目录下server.xml中看出。下面是删除了注释的server.xml(Tomcat版本为8.0.47)
<?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" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
通过下面这张结构图,可以更加清晰的理解配置文件server.xml
Host
- 一个 Host 配置即为一个虚拟主机,可以通过appBase属性配置web项目的目录
- appBase属性所指向的目录应该是准备用于存放一组 Web 应用程序的目录,而不是具体某个 Web 应用程序的目录本身
Context
- 具体的Web 应用程序,对应着平时开发的一套web程序
8005端口
- tomcat监听的关闭端口,就是说这个端口负责监听关闭tomcat的请求。
- 当执行shutdown.sh关闭tomcat时,就是通过8005端口发送SHUTDOWN命令。
- 用telnet向8005端口发送SHUTDOWN命令,也可以关闭tomcat,如果这个端口没被监听,那么shutdown.sh脚本就是无效的。
8009端口
- AJP协议端口
- AJP建立TCP连接后一般长时间保持,从而减少HTTP反复进行TCP连接和断开的开销
8080端口
- connector监听HTTP请求的端口
Connector架构
Connector就是使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的,Http11NioProtocol使用的是NioSocket来连接的。
其中ProtocolHandler由包含了三个部件:Endpoint、Processor、Adapter。
- Endpoint:处理底层Socket的网络连接
- Processor:将Endpoint接收到的Socket封装成Tomcat Request
- Adapter:将Tomcat Request封装成ServletRequest,给容器处理
Socket封装成Tomcat Request细节流程如下所示:
Container架构
- Engine:容器,一个Service最多只能有一个Engine
- Host:虚拟主机
- Context:应用程序
- Wrapper:每一Wrapper封装着一个Servlet
看到这里我们知道Container是什么,但还是不知道Container是如何处理请求的,以及处理完之后是如何将处理完的结果返回给Connector的。
别急,下边就开始探讨一下Container是如何进行处理的。
Container处理流程
容器通过Pipeline-Valve责任链模式来处理请求
但是,Pipeline-Valve使用的责任链模式和普通的责任链模式有些不同,区别主要有以下两点:
- 每个Pipeline都有特定的Valve,而且是在管道的最后一个执行,这个Valve叫做BaseValve,BaseValve是不可删除的
- 在上层容器的管道的BaseValve中会调用下层容器的管道
Container包含四个子容器,而这四个子容器对应的BaseValve分别是:
- StandardEngineValve
- StandardHostValve
- StandardContextValve
- StandardWrapperValve
Pipeline的处理流程图如下:
-
Connector在接收到请求后会首先调用最顶层容器的Pipeline来处理,这里的最顶层容器的Pipeline就是EnginePipeline(Engine的管道)。
-
在Engine的管道中依次会执行EngineValve1、EngineValve2等等,最后会执行StandardEngineValve,在StandardEngineValve中会调用Host管道,然后再依次执行Host的HostValve1、HostValve2等,最后在执行StandardHostValve,然后再依次调用Context的管道和Wrapper的管道,最后执行到StandardWrapperValve。
-
当执行到StandardWrapperValve的时候,会在StandardWrapperValve中创建FilterChain,并调用其doFilter方法来处理请求,这个FilterChain包含着我们配置的与请求相匹配的Filter和Servlet,其doFilter方法会依次调用所有的Filter的doFilter方法和Servlet的service方法,这样请求就得到了处理。
-
当所有的Pipeline-Valve都执行完之后,并且处理完了具体的请求,这个时候就可以将返回的结果交给Connector了,Connector在通过Socket的方式将结果返回给客户端。
结束啦