前言
Tomcat是目前应用比较多的servlet容器,有复杂的结构,想要了解它,就应该顺着开发者设计之初的思路来,先了解整体的结构,对整体有了一定的掌控后,再逐个分析,了解感兴趣的细节。
架构设计
简单介绍各个模块:
-
Server:服务器的意思,代表整个tomcat服务器,一个tomcat只有一个Server;
-
Service:Server中的一个逻辑功能层, 一个Server可以包含多个Service;
-
Connector:称作连接器,是Service的核心组件之一,一个Service可以有多个Connector,主要是连接客户端请求;
-
Container:Service的另一个核心组件,按照层级有Engine,Host,Context,Wrapper四种,一个Service只有一个Engine,其主要作用是执行业务逻辑;
server.xml配置文件
<?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>
Tomcat启动流程
启动Tomcat会是运行startup.bat或者startup.sh文件,这两个文件最后都会调用,org.apache.catalina.startup包下面Bootstrap类的main方法。
main方法先实例化了一个Bootstrap实例,接着调用了init方法。init方法是生命周期方法,接着看init的具体实现。
init方法,先初始化了类加载器。initClassLoaders方法具体实现如下:
Tomcat执行的是start操作,调用完init方法后,会执行load方法。
load方法通过反射调用Catalina类的load方法。
load方法中比较重要的方法是createStartDigester(),createStartDigester方法主要的作用就是帮我们实例化了所有的服务组件包括server,service和connect。
初始化操作完成后,接下来会执行catalina实例的start方法。Tomcat会默认加载org.apache.catalina.core.StandardServer作为Server的实例类。
在Server的start的方法里面会执行service的start方法。在createStartDigester()方法里面,会默认加载org.apache.catalina.core.StandardService类。会接着调用Service的start方法。
service中会调用connector的start方法。至此Tomcat启动完毕
Tomcat运行流程
假设来自客户的请求为:http://localhost:8080/test/index.jsp.请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得,然后
-
Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应
-
Engine获得请求localhost:8080/test/index.jsp,匹配它所有虚拟主机Host
-
Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
-
localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
-
Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
-
path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
-
Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类,构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
-
Context把执行完了之后的HttpServletResponse对象返回给Host
-
Host把HttpServletResponse对象返回给Engine
-
Engine把HttpServletResponse对象返回给Connector
-
Connector把HttpServletResponse对象返回给客户browser
Server
Server是Tomcat最顶层的容器,代表着整个服务器,即一个Tomcat只有一个Server,Server中包含至少一个Service组件,用于提供具体服务。这个在配置文件中也得到很好的体现(port=”8005” shutdown=”SHUTDOWN”是在8005端口监听到”SHUTDOWN”命令,服务器就会停止)。
Tomcat中其标准实现是:org.apache.catalina.core.StandardServer类,继承LifecycleMBeanBase,,tomcat为所有的组件都提供了生命周期管理。
Service
在conf/server.xml文件中,可以看到Service组件包含了Connector组件和Engine组件(前面有提过,Engine就是一种容器),即Service相当于Connector和Engine组件的包装器,将一个或者多个Connector和一个Engine建立关联关系。在默认的配置文件中,定义了一个叫Catalina 的服务,它将HTTP/1.1和AJP/1.3这两个Connector与一个名为Catalina 的Engine关联起来。
一个Server可以包含多个Service(它们相互独立,只是公用一个JVM及类库),一个Service负责维护多个Connector和一个Container。其标准实现是StandardService。
Connector
Connector主要负责处理与客户端的通信,Connector的实例用于监听端口,接受来自客户端的请求并将请求转交给Engine处理,同时将来自Engine的答复返回给客户端。
Tomcat源码中与connector相关的类位于org.apache.coyote包中,Connector分为以下几类:
Http Connector, 基于HTTP协议,负责建立HTTP连接。它又分为BIO Http Connector与NIO Http Connector两种,后者提供非阻塞IO与长连接Comet支持。默认情况下,Tomcat使用的就是这个Connector。
AJP Connector, 基于AJP协议,AJP是专门设计用来为tomcat与http服务器之间通信专门定制的协议,能提供较高的通信速度和效率。如与Apache服务器集成时,采用这个协议。
APR HTTP Connector, 用C实现,通过JNI调用的。主要提升对静态资源(如HTML、图片、CSS、JS等)的访问性能。现在这个库已独立出来可用在任何项目中。Tomcat在配置APR之后性能非常强劲。
Connector使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的(tomcat9已经删除了这个类,不再采用BIO的方式),Http11NioProtocol使用的是NioSocket来连接的。
其中ProtocolHandler由包含了三个部件:Endpoint、Processor、Adapter。
1. Endpoint用来处理底层Socket的网络连接,Processor用于将Endpoint接收到的Socket封装成Request(这个Request和ServletRequest无关),Adapter充当适配器,用于将Request转换为ServletRequest交给Container进行具体的处理。
2. Endpoint由于是处理底层的Socket网络连接,因此Endpoint是用来实现TCP/IP协议的,而Processor用来实现HTTP协议的,Adapter将请求适配到Servlet容器进行具体的处理。
3. Endpoint的抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时,Handler用于处理接收到的Socket,在内部调用Processor进行处理。
下面我们以org.apache.coyote.http11.Http11AprProtocol为例说明Connector的工作流程。
①它将工作委托给AprEndpoint类。
public Http11AprProtocol() {
endpoint = new AprEndpoint(); // 主要工作由AprEndpoint来完成
cHandler = new Http11ConnectionHandler(this); // inner class
((AprEndpoint) endpoint).setHandler(cHandler);
②在AprEndpoint.Acceptor类中的run()方法会接收一个客户端新的连接请求.
protected class Acceptor extends AbstractEndpoint.Acceptor {
private final Log log = LogFactory.getLog(AprEndpoint.Acceptor.class);
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
③在AprEndpoint类中,有一个内部接口Handler,该接口定义如下:
public interface Handler extends AbstractEndpoint.Handler {
public SocketState process(SocketWrapper<Long> socket,
SocketStatus status);
}
④在Http11AprProtocol类中实现了AprEndpoint中的Handler接口,
protected static class Http11ConnectionHandler
extends AbstractConnectionHandler<Long,Http11AprProcessor> implements Handler {
并调用Http11AprProcessor类(该类实现了ActionHook回调接口)。
protected Http11AprProcessor createProcessor() {
Http11AprProcessor processor = new Http11AprProcessor(
Container
Tomcat提供了一个Container接口来抽象容器,并且细分了4种类型的容器,分别是Engine、Host、Context和Wrapper,对应不同的概念层次。
· Engine:表示整个Catalina的servlet引擎
· Host:表示一个拥有数个上下文的虚拟主机
· Context:表示一个Web应用,一个context包含一个或多个wrapper
· Wrapper:表示一个独立的servlet