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

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

总的流程如下:

时间: 2024-10-23 23:26:11

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

Tomcat请求处理过程(Tomcat源码解析五)

前面已经分析完了Tomcat的启动和关闭过程,本篇就来接着分析一下Tomcat中请求的处理过程. 在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求->建立Socket连接->通过Socket读取数据->根据http协议解析数据->调用后台服务完成响应,详细的流程图如上图所示,等读者读完本篇,应该就清楚了上图所表达的意思.Tomcat既是一个HttpServer也是一个Servlet 容器,那么这里必然也涉及到如上过程,首先根据HTTP协议

tomcat源码解析

tomcat+springmvc源码解析视频教程 https://study.163.com/course/introduction.htm?courseId=1209399899&trace_c_p_k2=aa9267699c40462ba96533f2f8982e4c 原文地址:https://blog.51cto.com/3921161/2430346

[Spark內核] 第42课:Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践

本课主题 Broadcast 运行原理图 Broadcast 源码解析 Broadcast 运行原理图 Broadcast 就是将数据从一个节点发送到其他的节点上; 例如 Driver 上有一张表,而 Executor 中的每个并行执行的Task (100万个Task) 都要查询这张表的话,那我们通过 Broadcast 的方式就只需要往每个Executor 把这张表发送一次就行了,Executor 中的每个运行的 Task 查询这张唯一的表,而不是每次执行的时候都从 Driver 中获得这张表

spring boot 源码解析 启动流程

spring boot 源码解析 启动流程 在面试过程中经常被问到过spring boot的启动流程,今天就翻一下源码整体看一下: 首先,新建一个启动类,可以看到是首先调用的SpringApplication的静态方法run @SpringBootApplication public class SourceReadApplillcation { public static void main(String[] args) { SpringApplication.run(SourceReadAp

grunt源码解析:整体运行机制&grunt-cli源码解析

前端的童鞋对grunt应该不陌生,前面也陆陆续续的写了几篇grunt入门的文章.本篇文章会更进一步,对grunt的源码进行分析.文章大体内容内容如下: grunt整体设计概览 grunt-cli源码分析 grunt-cli模块概览 grunt-cli源码分析 写在后面 grunt整体设计概览 grunt主要由三部分组成.其中,grunt-cli是本文的讲解重点 grunt-cli:命令行工具,调用本地安装的grunt来运行任务,全局安装. grunt:本地grunt,一般安装在项目根目录下.主要

netty服务端启动--ServerBootstrap源码解析

netty服务端启动--ServerBootstrap源码解析 前面的第一篇文章中,我以spark中的netty客户端的创建为切入点,分析了netty的客户端引导类Bootstrap的参数设置以及启动过程.显然,我们还有另一个重要的部分--服务端的初始化和启动过程没有探究,所以这一节,我们就来从源码层面详细分析一下netty的服务端引导类ServerBootstrap的启动过程. spark中netty服务端的创建 我们仍然以spark中对netty的使用为例,以此为源码分析的切入点,首先我们看

Spring Boot 启动源码解析系列六:执行启动方法一

1234567891011121314151617181920212223242526272829303132333435363738394041424344 public ConfigurableApplicationContext (String... args) { StopWatch stopWatch = new StopWatch(); // 开始执行,记录开始时间 stopWatch.start(); ConfigurableApplicationContext context =

tomcat的启动过程(Tomcat源码解析(三))

Tomcat组件生命周期管理 在Tomcat总体结构 (Tomcat源代码解析之二)中,我们列出了Tomcat中Server,Service,Connector,Engine,Host,Context的继承关系图,你会发现它们都实现了org.apache.catalina.Lifecycle接口,而org.apache.catalina.util.LifecycleBase采用了模板方法模式来对所有支持生命周期管理的组件的生命周期各个阶段进行了总体管理,每个需要生命周期管理的组件只需要继承这个基

Tomcat源码解析-启动过程分析之主干流程

Tomcat启动入口就在脚本startup.sh中,具体脚本可以看tomcat的源码,这个启动脚本主要用来判断环境,找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行.catalina.sh start 最终会执行org.apache.catalina.startup.Bootstrap中的main方法,并把start参数传入.以后分析Tomcat关闭的时候,也是一个套路,最终都会调用到org.apache.catalina.startup.Bootstrap的mai