频道栏目
首页 > 资讯 > Java > 正文

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

16-06-21        来源:[db:作者]  
收藏   我要投稿

本文是我阅读了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"]:

 

 

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

 

 

接下来程序会执行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组件。

 

 

 

 

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

 

 

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

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

 

 

StandardService的initialize():

 

 

 

 

初始化connector,Connector的initialize():

 

 

 

 

初始化协议,Http11Protocol的init():

 

 

Endpoint的初始化,Endpoint的init():

 

 

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

初始化的流程:

 

 

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

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

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

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

来看看Catalina的start():

 

 

StandardServer的start():

 

 

下面就看看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线程池

 

 

StandardService下的容器有Container,这个Container其实就是Engine,Engine下的容器有Host,Host下的容器有Context。

每个容器下都有Pipeline、Value、Realm等等配置。

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

 

 

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

 

 

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

 

 

 

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

 

 

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

 

 

启动jkMain:

 

 

最后,在channelSocket的init()方法中启动TP-Monitor和TP-Processor线程池:

 

 

 

 

此时,我们可以看到TP-Monitor和TP-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)Http11ConnectionHandler的process()方法处理:

 

 

4)Http11Processor的process():

 

 

 

 

5)CoyoteAdapter的service():

 

 

6)StandardEngineValve的invoke():

 

 

7)StandardHostValve的invoke():

 

 

8)StandardContextValve的invoke():

 

 

9)StandardWraValve的invoke():

 

 

总的流程如下:

 

相关TAG标签
上一篇:【C++】泛型编程基础:模板通识
下一篇:JavaSE学习57:GUI编程之事件模型(二)
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站