一、Tomcat的顶层结构及启动过程
1.Tomcat的顶层结构
Tomcat中最顶层的容器叫Server,代表整个服务器,Server至少包含一个Service用于具体的服务。Service主要包含两部分,Connector和Container。Connector用于处理连接相关的事情,并提供Socket与request、response的转换,Container用于封装和管理Servlet,以及具体处理request的请求。一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以包含多个Connector(因为一个服务可以有多个连接,如同时提供http和https的连接,也可以提供相同协议但不同端口的连接)。结构如下图所示
Tomcat里的Server由org.apache.catalina.startup.Catalina来管理,Catalina是整个Tomcat的管理类,它里面的三个方法load、start、stop分别来管理整个服务器的生命周期,load方法用于根据conf/server.xml文件创建Server并调用Server的init方法进行初始化,start方法用于启动服务器,stop方法用于停止服务器。
不过Tomcat的入口main方法并不在Catalina类里,而是在org.apache.catalina.startup.Bootstrap中。
2.Bootstrap的启动过程
Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用的Bootstrap的main方法。
Bootstrap的main方法非常简单,只有两部分内容:首先新建了Bootstrap,并执行init方法初始化;然后处理main方法传入的命令,如果args参数为空,默认执行start。
在init方法里初始化了ClassLoader,并用ClassLoader创建了Catalina实例,然后赋给catalinaDaemon变量,后面对命令的操作都要使用catalinaDaemen来具体执行。
对start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。这三个方法内部都调用了Catalina的相应方法进行具体执行,只不过是用反射来调用的。
3.Catalina的启动过程
Catalina的启动主要是调用setAwait、load和start方法来完成的。setAwait方法用于设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入;load方法用于加载配置文件,创建并初始化Server;start方法用于启动服务器。
4.Server的启动过程
server接口中提供addService(Service service)、removeService(Service service)来添加和删除Service,Service的init方法和start方法分别循环调用了每个service的init方法和start方法来启动所有service。
Server的默认实现是org.apache.catalina.core.StandardServer,StandardSever继承自LifecycleMBeanBase,LifecycleMBeanBase又继承自LifecycleBase,init和start方法就定义在LifecycleBase中,LifecycleBase里的init方法和start方法又调用initInternal方法和startInternal方法,这两个方法都是模板方法,由子类具体实现,所以调用StandardServer的init和start方法时会执行StandardServer自己的initInternal和startInternal方法,这就是tomcat生命周期的管理方式。StandardServer中的initInternal和startInternal方法分别循环调用了每一个service的start和init方法。
5.Service的启动过程
Service的默认实现是org.apache.catalina.core.StandardService,StandardService也继承自LifeCycleMBeanBase类,所以init和start方法最终也会调用initInternal和startInternal方法。
StandardService中的initInternal和startInternal方法主要调用container、executors、mapperListener、connectors的init和start方法。这里的container和connectors前面已经介绍过了。mapperListener是Mapper的监听器,可以监听container容器的变化,executors是在connectors中管理线程的线程池,在server.xml配置文件中有参考用法。
这样Connector就配置了一个叫tomcatThreadPool的线程池,最多可以同时启动150个线程,至少要有4个可用的线程。
整个Tomcat的启动流程如下图所示
二、Tomcat的生命周期管理
1.Lifecycle接口
Tomcat通过org.apache.catalina.Lifecycle接口统一管理生命周期,所有有生命周期的组件都要事先Lifecycle接口。Lifecycle接口一共做了4件事:
2.LifecycleBase
Lifecycle的默认实现是org.apache.catalina.util.LifecycleBase,所有实现了生命周期的组件都直接或间接继承自LifecycleBase,LifecycleBase为Lifecycle里的接口方法提供了默认实现:监听器管理是专门使用了一个LifecycleSupport类来完成的,LifecycleSupport中定义了一个LifecycleListener数组类型的属性来保存所有的监听器,然后定义了添加、删除、查找和执行监听器的方法;生命周期方法中设置了相应的状态并调用了相应的模板方法,ini、start、stop和destroy所对应的模板方法分别是initInternal、startInternal、stopInternal和destroyInternal方法,这四个方法由子类具体实现,所以对于子类来说,执行生命周期处理的方法就是initInternal、startInternal、stopInternal和destroyInternl。
三、Container分析
1.ContainerBase的结构
Container是Tomcat中容器的接口,通常使用的Servlet就封装在其子接口Wrapper中。
Container一共有四个子接口Engine、Host、Context、Wrapper和一个默认实现类ContainerBase,每个子接口都是一个容器,这四个子容器都有一个对应的StandardXXX实现类,并且这些实现类都继承ContainerBase类。另外Container还继承Lifecycle接口,而且ContainerBase间接继承了LifecycleBase,所以Engine、Host、Context、Wrapper四个子容器都符合前面讲过的Tomcat生命周期管理模式。结构图如下所示:
2.Container的四个子容器
Container的子容器Engine、Host、Context、Wrapper是逐层包含的关系,其中Engine是最顶层,每个Service最多只能有一个Engine,Engine里面可以有多个Host,每个Host下可以有多个Context,每个Context下可以有多个Wrapper,它们的关系如下图所示:
四个容器的作用分别是:
Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine。
Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点。
Context:代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件。
Wrapper:每个Wrapper封装着一个Servlet。
Context和Host的区别是Context表示一个应用。例如,默认配置下webapps下的每个目录都是一个应用,其中ROOT目录中存放着主应用,其他目录存放着别的子应用,而整个webapps是一个站点。假如www.excelib.com域名对应着webapps目录所代表的的站点,其中的ROOT目录里的应用就是主应用,访问时直接使用域名就可以,而webapps/test目录存放的是test子应用,访问时需要用www.excelib.com/test,每一个应用对应一个Context,所有webapps下的应用都属于www.excelib.com站点,而blog.excelib.com则是另外一个站点。属于另外一个Host。
3.Container的启动
Container的启动是通过init和start方法来完成的,这两个方法会在Tomcat启动时被Service调用。Container也是按照Tomcat的生命周期来管理的,init和start方法也会调用initInternal和startInternal方法来具体处理,不过Container和Tomcat整体结构启动的过程稍微有点不一样,主要有三点区别:
四、Pipeline-Value管道
Container处理请求是使用Pipeline-Value管道来处理的。
1.Pipeline-Value处理模式
Pipeline-Value是责任链模式,责任链模式是指在一个请求处理的过程中有多个处理这一次对请求进行处理,每个处理者负责做自己相应的处理,处理完成后将处理后的请求返回,再让下一个处理者继续处理。
Pipeline-Value的管道模型和普通的责任链模式有些不同,主要区别有两点:
四个容器的BaseValue分别是StandardEngineValue、StandardHostValue、StandardContextValue和StandardWrapperValue,整个处理的流程如下图所示:
在Engine的管道中依次执行Engine的各个Value,最后执行StandEngineValue用于调用Host的管道,然后执行Host的Value,这样依次类推最后执行Wrapper管道中的StandardWrapperValue。
在Filter中用到的FilterChain其实就是这种模式,FilterChain相当于Pipeline,每个Filter都相当于一个Value,Servlet相当于最后的BaseValue。
五、Connector分析
Connector用于接收请求并将请求封装成Request和Response来具体处理,最底层是使用Socket来进行连接的,Request和Response是按照HTTP协议来封装的,所以Connector同时实现了TCP/IP协议和HTTP协议,Request和Response封装完之后交给Container进行处理,Container就是Servlet的容器,Container处理完之后返回给Connector,最后Connector使用Socket将处理结果返回给客户端。
1.Connector的结构
Connector中具体使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如,HttpllProtocol使用的是普通Socket来连接的,HttpllNioProtocol使用的是NioSocket来连接的。
ProtocolHandler里面有三个非常重要的组件:Endpoint、Processor和Adapter。Endpoint用于处理底层Socket的网络连接,Processor用于将Endpoint接收到的Socket封装成Request,Adapter用于将封装好的Request交给Container进行具体处理。也就是说Endpoint用来实现TCP/IP协议,Processor用来实现HTTP协议,Adapter将请求适配到Servlet容器进行具体处理。
Endpoint的抽象实现AbstractEndPoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步request的超时,Handler用于处理接收到的Socket,在内部调用了Processor进行处理。
Connector的结构如下图所示: