码迷,mamicode.com
首页 > 其他好文 > 详细

Tomcat是如何启动及运行—对tomcat的源码解析

时间:2016-06-21 06:55:30      阅读:287      评论:0      收藏:0      [点我收藏+]

标签:

本文是我阅读了Tomcat源码后的一些心得。主要是讲解Tomcat的系统框架,启动流程已经运行过程。若有错漏之处,敬请批评指教。

     先给出几个问题:

tomcat作为一个应用服务器的程序入口在哪里?

tomcat的整体组件结构是什么样的?

tomcat是什么时候及如何创建线程来处理请求的?

tomcat的配置文件context.xml,server.xml,tomcat-users.xml,web.xml什么时候加载的及作用是什么?

最后,tomcat是如何启动运行的?

我通过源码来分析这些问题。

       tomcat作为最常见的应用服务器,对ServerSocketSocket封装使用TCP链接达到通信的目的,我从它的程序入口开始跟踪代码,看它是如何启动的。

     Tomcat组件有ServerServiceContainerConnectorEngineHostContextProtocolHandlerEndPoint

Server一个Server元素代表一整个CatalinaServlet容器,也就是说Server是最顶级的容器,它包含一个或多个Service,这个在后面我们可以看到;

Service由一个或者多个Connector组成,以及一个Engine,负责处理所有Connector所获得的客户请求;

Connector连接器,一个Connector将在某个指定端口上侦听客户请求,并将获得的请求交给Engine来处理,从Engine处获得回应并返回客户;

EngineEngine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理;

HostHost获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理;

Context一个Context对应于一个WebApplication,一个WebApplication由一个或者多个Servlet组成(这个就是我们熟悉的上下文对象了);

ContainerEngineHostContext的父类或父接口,这里使用了Composite组合模式,调用它们的start()方法时都会调用父类的此方法;

ProtocolHandler协议处理器,连接器持有用来控制网络端口监听组件运行;

EndPoint负责监控网络端口的组件,启动一个ServerSocket,不间断监听来自客户端的请求。

     当我们启动Tomcat时:程序会找到Bootstrapmain()方法,同时把args置为["start"]:

技术分享

然后程序会执行init()方法,设置tomcat运行的环境,初始化类加载器,创建Catalina对象:

技术分享

接下来程序会执行load()start()方法,这是tomcat启动过程中最重要的两个过程,load()其实是初始化一系列的组件,上面所列的组件都会进行初始化,包括加载配置文件都是在这个过程中实现的;而start()方法就是启动这一系列的组件,当所有组件都启动后,tomcat也就启动完成了。

从这里看,Tomcat启动过程可以简化为3个步骤:

1Bootstrapinit()方法,其实就是设置运行环境和初始化类加载器;

2Bootstrapload()方法,加载相关配置文件,初始化几个主要的顶层组件实例,也就是服务器初始化。

3)启动那些有生命周期的组件,监听用户请求,也就是启动服务器。

        load()方法其实是通过反射来执行Catalinaload()方法,Catalinaload()方法主要作用:

              initDirs()方法初始化tomcat的安装路径;

               createStartDigester()方法解析xml文件;

              getServer().initialize()方法初始化Server组件。

技术分享

技术分享

接下来我们看一下server.xml里面的信息到底是什么?为什么能创建server对象呢?

技术分享

这个就是server.xml的内容,我们可以清晰的看到server下面包含serviceservice下面包含connectorconnector下面包含enginehostcontext,这些就是我们上面所说的tomcat的组件,他们的对应关系也是这样层层包含的关系。

           接下来看看Server真正的初始化过程:StandardServerinitialize():

技术分享

StandardServiceinitialize()

技术分享

技术分享

初始化connectorConnectorinitialize()

技术分享

技术分享

初始化协议,Http11Protocolinit()

技术分享

Endpoint的初始化,Endpointinit()

技术分享

根据上面的server.xml,connector有两个,所以会初始化两个connectorProtocolHandlerendpoint;

初始化的流程:

技术分享

所以我们可以知道Bootstrap#load()—>Catalina#load()—>StandardServer#load()—>StandardService#initialize()—>Connector#initialize()—>Http11Protocol#init()—>Endpoint#init()

至此,tomcat的初始化全部完成。



接下来开始启动tomcat服务器:

回到Bootstrap来,Bootstrapstart()方法通过反射来调用Catalina方法的start()方法

来看看Catalinastart()

技术分享

StandardServerstart()

技术分享

下面就看看StandardService对象的start方法:

首先启动Container容器,在server.xml中我们知道Container中含有EngineHostContext,所以这里会启动这三个容器,Service中的这个ContainerEngine,所以首先会启动EngineEngine中有HostContext,它们又都属于Container,所以,这里充分的利用了组合模式,这个过程中会创建background后台守护线程,周期性的检查Session是否超时

然后启动执行器executors,这里executors一般为null

最后启动所有的Connector,这个过程会启动两组ProtocolHandlerEndpoint,这个过程是很重要的一个过程,会创建所有的请求线程池,首先先创建一个Acceptor线程来监听当前线程是否够用,若不够用则会创建线程池用来处理http请求;另外一组ProtocolHandlerEndpoint是用来处理apj请求,会创建TP-MonitorTP-Processor线程池

技术分享

StandardService下的容器有Container,这个Container其实就是EngineEngine下的容器有HostHost下的容器有Context

每个容器下都有PipelineValueRealm等等配置。

接下来就看看如何启动这些各个容器的:

技术分享

父类的start()方法:每个container子类都会调用,根据当前对象读取不同的children,然后递归调用childrenstart()方法

技术分享

技术分享

启动background后台守护线程,周期性的检查Session是否超时:

技术分享

此时我们可以清楚的看到background线程已经启动

技术分享



所有的container启动完毕后,接下来回到StandardServicestart()来:接下来执行Connectorstart()

技术分享

Http11Protocolstart():委托给JioEndpoint调用start()方法,真正跟serverSocket有关的都在endpoint中执行

技术分享

JIoEndpointstart():启动endpoint实际上是启动一个acceptor线程,这个线程很重要,在这个线程里面判断当前的http线程是否足够,若不够则从线程池中创建线程来处理http请求,所以,就是在这里创建的线程来处理请求的

技术分享

接下来我们看看Accptor类的run()方法中究竟写了什么:

技术分享

技术分享

到这里,我们就创建了acceptor线程和http线程池了。

技术分享



接下来处理另外一个connector:这里用的协议处理器是JkCoyoteHandlerJkCoyoteHandlerstart()

技术分享

启动jkMain

技术分享

最后,在channelSocketinit()方法中启动TP-MonitorTP-Processor线程池:

技术分享

技术分享

此时,我们可以看到TP-MonitorTP-Processor线程已经启动:

技术分享



所以我们可以知道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的初始化全部完成。

技术分享

至此,tomcat服务器已完全启动了。

启动的时序图如下:

技术分享

TomcatServer处理一个http请求的过程

1)当来了一个http请求,acceptor线程先判断现在已存在的线程是否足够,若不足够,则创建一个线程来处理请求,足够就使用已存在的;

技术分享

技术分享

2)SocketProcessor来处理请求

技术分享

3)Http11ConnectionHandlerprocess()方法处理:

技术分享

4)Http11Processorprocess()

技术分享

技术分享

5)CoyoteAdapterservice()

技术分享

6)StandardEngineValveinvoke()

技术分享

7)StandardHostValveinvoke()

技术分享

8)StandardContextValveinvoke()

技术分享

9)StandardWraValveinvoke()

技术分享

总的流程如下:

技术分享

Tomcat是如何启动及运行—对tomcat的源码解析

标签:

原文地址:http://blog.csdn.net/ningdunquan/article/details/51721469

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!