本文是我阅读了Tomcat源码后的一些心得。主要是讲解Tomcat的系统框架,启动流程已经运行过程。若有错漏之处,敬请批评指教。
先给出几个问题:
tomcat作为一个应用服务器的程序入口在哪里?
tomcat的整体组件结构是什么样的?
tomcat是什么时候及如何创建线程来处理请求的?
tomcat的配置文件context.xml,server.xml,tomcat-users.xml,web.xml什么时候加载的及作用是什么?
最后,tomcat是如何启动运行的?
我通过源码来分析这些问题。
tomcat作为最常见的应用服务器,对ServerSocket、Socket封装使用TCP链接达到通信的目的,我从它的程序入口开始跟踪代码,看它是如何启动的。
Tomcat组件有Server,Service,Container,Connector,Engine,Host,Context,ProtocolHandler,EndPoint等
Server:一个Server元素代表一整个CatalinaServlet容器,也就是说Server是最顶级的容器,它包含一个或多个Service,这个在后面我们可以看到;
Service:由一个或者多个Connector组成,以及一个Engine,负责处理所有Connector所获得的客户请求;
Connector:连接器,一个Connector将在某个指定端口上侦听客户请求,并将获得的请求交给Engine来处理,从Engine处获得回应并返回客户;
Engine:当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理;
Host:当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理;
Context:一个Context对应于一个WebApplication,一个WebApplication由一个或者多个Servlet组成(这个就是我们熟悉的上下文对象了);
Container:是Engine,Host,Context的父类或父接口,这里使用了Composite组合模式,调用它们的start()方法时都会调用父类的此方法;
ProtocolHandler:协议处理器,连接器持有用来控制网络端口监听组件运行;
EndPoint:负责监控网络端口的组件,启动一个ServerSocket,不间断监听来自客户端的请求。
当我们启动Tomcat时:程序会找到Bootstrap的main()方法,同时把args置为["start"]:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090502130.png)
然后程序会执行init()方法,设置tomcat运行的环境,初始化类加载器,创建Catalina对象:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090502131.png)
接下来程序会执行load()和start()方法,这是tomcat启动过程中最重要的两个过程,load()其实是初始化一系列的组件,上面所列的组件都会进行初始化,包括加载配置文件都是在这个过程中实现的;而start()方法就是启动这一系列的组件,当所有组件都启动后,tomcat也就启动完成了。
从这里看,Tomcat启动过程可以简化为3个步骤:
1)Bootstrap的init()方法,其实就是设置运行环境和初始化类加载器;
2)Bootstrap的load()方法,加载相关配置文件,初始化几个主要的顶层组件实例,也就是服务器初始化。
3)启动那些有生命周期的组件,监听用户请求,也就是启动服务器。
load()方法其实是通过反射来执行Catalina的load()方法,Catalina的load()方法主要作用:
initDirs()方法初始化tomcat的安装路径;
createStartDigester()方法解析xml文件;
getServer().initialize()方法初始化Server组件。
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090502132.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090502133.png)
接下来我们看一下server.xml里面的信息到底是什么?为什么能创建server对象呢?
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503134.png)
这个就是server.xml的内容,我们可以清晰的看到server下面包含service,service下面包含connector,connector下面包含engine,host和context,这些就是我们上面所说的tomcat的组件,他们的对应关系也是这样层层包含的关系。
接下来看看Server真正的初始化过程:StandardServer的initialize():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503135.png)
StandardService的initialize():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503136.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503137.png)
初始化connector,Connector的initialize():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503138.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503139.png)
初始化协议,Http11Protocol的init():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503140.png)
Endpoint的初始化,Endpoint的init():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503141.png)
根据上面的server.xml,connector有两个,所以会初始化两个connector,ProtocolHandler和endpoint;
初始化的流程:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503142.png)
所以我们可以知道Bootstrap#load()—>Catalina#load()—>StandardServer#load()—>StandardService#initialize()—>Connector#initialize()—>Http11Protocol#init()—>Endpoint#init()
至此,tomcat的初始化全部完成。
接下来开始启动tomcat服务器:
回到Bootstrap来,Bootstrap的start()方法通过反射来调用Catalina方法的start()方法
来看看Catalina的start():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503143.png)
StandardServer的start():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090503144.png)
下面就看看StandardService对象的start方法:
首先启动Container容器,在server.xml中我们知道Container中含有Engine,Host,Context,所以这里会启动这三个容器,Service中的这个Container是Engine,所以首先会启动Engine,Engine中有Host和Context,它们又都属于Container,所以,这里充分的利用了组合模式,这个过程中会创建background后台守护线程,周期性的检查Session是否超时
然后启动执行器executors,这里executors一般为null
最后启动所有的Connector,这个过程会启动两组ProtocolHandler和Endpoint,这个过程是很重要的一个过程,会创建所有的请求线程池,首先先创建一个Acceptor线程来监听当前线程是否够用,若不够用则会创建线程池用来处理http请求;另外一组ProtocolHandler和Endpoint是用来处理apj请求,会创建TP-Monitor和TP-Processor线程池
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504147.png)
StandardService下的容器有Container,这个Container其实就是Engine,Engine下的容器有Host,Host下的容器有Context。
每个容器下都有Pipeline、Value、Realm等等配置。
接下来就看看如何启动这些各个容器的:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504149.png)
父类的start()方法:每个container子类都会调用,根据当前对象读取不同的children,然后递归调用children的start()方法
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504150.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504151.png)
启动background后台守护线程,周期性的检查Session是否超时:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504152.png)
此时我们可以清楚的看到background线程已经启动
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504153.png)
所有的container启动完毕后,接下来回到StandardService的start()来:接下来执行Connector的start():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504154.png)
Http11Protocol的start():委托给JioEndpoint调用start()方法,真正跟serverSocket有关的都在endpoint中执行
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090504156.png)
JIoEndpoint的start():启动endpoint实际上是启动一个acceptor线程,这个线程很重要,在这个线程里面判断当前的http线程是否足够,若不够则从线程池中创建线程来处理http请求,所以,就是在这里创建的线程来处理请求的
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505157.png)
接下来我们看看Accptor类的run()方法中究竟写了什么:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505158.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505159.png)
到这里,我们就创建了acceptor线程和http线程池了。
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505160.png)
接下来处理另外一个connector:这里用的协议处理器是JkCoyoteHandler,JkCoyoteHandler的start():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505161.png)
启动jkMain:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505162.png)
最后,在channelSocket的init()方法中启动TP-Monitor和TP-Processor线程池:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505163.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505164.png)
此时,我们可以看到TP-Monitor和TP-Processor线程已经启动:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505165.png)
所以我们可以知道tomcat容器的启动过程:
Bootstrap#start()—>Catalina#start()—>StandardServer#start()—>StandardService#start()—>Connector#start()—>Http11Protocol#start()—>Endpoint#init()—>acceptorThread.start()启动请求线程池
StandardService#start()—>StandardEngine#start()—>ContainerBase#start()—>StandardHost#start()—>ContainerBase#start()—>StandardContext#start()—>ContainerBase#start()(启动守护线程)
至此,tomcat的初始化全部完成。
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505166.png)
至此,tomcat服务器已完全启动了。
启动的时序图如下:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090505167.png)
TomcatServer处理一个http请求的过程
1)当来了一个http请求,acceptor线程先判断现在已存在的线程是否足够,若不足够,则创建一个线程来处理请求,足够就使用已存在的;
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506168.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506169.png)
2)SocketProcessor来处理请求
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506173.png)
3)Http11ConnectionHandler的process()方法处理:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506176.png)
4)Http11Processor的process():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506179.png)
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506182.png)
5)CoyoteAdapter的service():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506185.png)
6)StandardEngineValve的invoke():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506188.png)
7)StandardHostValve的invoke():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090506190.png)
8)StandardContextValve的invoke():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090507193.png)
9)StandardWraValve的invoke():
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090507199.png)
总的流程如下:
![\](http://www.2cto.com/uploadfile/Collfiles/20160621/20160621090507203.png)