面试官:谈谈 Tomcat 架构及启动过程,我一脸懵逼。。
Posted 程序员大咖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:谈谈 Tomcat 架构及启动过程,我一脸懵逼。。相关的知识,希望对你有一定的参考价值。
👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇
来源:https://github.com/c-rainstorm/blog/tree/master/tomcat
这个题目命的其实是很大的,写的时候还是很忐忑的,但我尽可能把这个过程描述清楚。因为这是读过源码以后写的总结,在写的过程中可能会忽略一些前提条件,如果有哪些比较突兀就出现,或不好理解的地方可以给我提 Issue,我会尽快补充修订相关内容。
很多东西在时序图中体现的已经非常清楚了,没有必要再一步一步的作介绍,所以本文以图为主,然后对部分内容加以简单解释。
绘制图形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension
本文对 Tomcat 的介绍以 Tomcat-9.0.0.M22 为标准。
Overview
Bootstrap 作为 Tomcat 对外界的启动类,在 $CATALINA_BASE/bin 目录下,它通过反射创建 Catalina 的实例并对其进行初始化及启动。
Catalina 解析 $CATALINA_BASE/conf/server.xml 文件并创建 StandardServer、StandardService、StandardEngine、StandardHost 等
StandardServer 代表的是整个 Servlet 容器,他包含一个或多个 StandardService
StandardService 包含一个或多个 Connector,和一个 Engine,Connector 和 Engine 都是在解析 conf/server.xml 文件时创建的,Engine 在 Tomcat 的标准实现是 StandardEngine
MapperListener 实现了 LifecycleListener 和 ContainerListener 接口用于监听容器事件和生命周期事件。该监听器实例监听所有的容器,包括 StandardEngine、StandardHost、StandardContext、StandardWrapper,当容器有变动时,注册容器到 Mapper。
Mapper 维护了 URL 到容器的映射关系。当请求到来时会根据 Mapper 中的映射信息决定将请求映射到哪一个 Host、Context、Wrapper。
Http11NioProtocol 用于处理 HTTP/1.1 的请求
NioEndpoint 是连接的端点,在请求处理流程中该类是核心类,会重点介绍。
CoyoteAdapter 用于将请求从 Connctor 交给 Container 处理。使 Connctor 和 Container 解耦。
StandardEngine 代表的是 Servlet 引擎,用于处理 Connector 接受的 Request。包含一个或多个 Host(虚拟主机), Host 的标准实现是 StandardHost。
StandardHost 代表的是虚拟主机,用于部署该虚拟主机上的应用程序。通常包含多个 Context (Context 在 Tomcat 中代表应用程序)。Context 在 Tomcat 中的标准实现是 StandardContext。
StandardContext 代表一个独立的应用程序,通常包含多个 Wrapper,一个 Wrapper 容器封装了一个 Servlet,Wrapper的标准实现是 StandardWrapper。
StandardPipeline 组件代表一个流水线,与 Valve(阀)结合,用于处理请求。StandardPipeline 中含有多个 Valve, 当需要处理请求时,会逐一调用 Valve 的 invoke 方法对 Request 和 Response 进行处理。特别的,其中有一个特殊的 Valve 叫 basicValve,每一个标准容器都有一个指定的 BasicValve,他们做的是最核心的工作。
StandardEngine 的是 StandardEngineValve,他用来将 Request 映射到指定的 Host;
StandardHost 的是 StandardHostValve, 他用来将 Request 映射到指定的 Context;
StandardContext 的是 StandardContextValve,它用来将 Request 映射到指定的 Wrapper;
StandardWrapper 的是 StandardWrapperValve,他用来加载 Rquest 所指定的 Servlet,并调用 Servlet 的 Service 方法。
Tomcat init
EngineConfig。LifecycleListener 的实现类,触发 Engine 的生命周期事件后调用,这个监听器没有特别大的作用,就是打印一下日志
HostConfig。LifecycleListener 的实现类,触发 Host 的生命周期事件后调用。这个监听器的作用就是部署应用程序,这包括 conf/// 目录下所有的 Context xml 文件 和 webapps 目录下的应用程序,不管是 war 文件还是已解压的目录。另外后台进程对应用程序的热部署也是由该监听器负责的。
Tomcat Start[Deployment]
触发 Host 的 BEFORE_START_EVENT 生命周期事件,HostConfig 调用其 beforeStart() 方法创建 CATALINA_BASE/webapps& $CATALINA_BASE/conf/// 目录。
触发 Host 的 START_EVENT 生命周期事件,HostConfig 调用其 start() 方法开始部署已在 CATALINA_BASE/webapps & $CATALINA_BASE/conf/// 目录下的应用程序。
解析 $CATALINA_BASE/conf/// 目录下所有定义 Context 的 XML 文件,并添加到 StandardHost。这些 XML 文件称为应用程序描述符。正因为如此,我们可以配置一个虚拟路径来保存应用程序中用到的图片,详细的配置过程请参考 开发环境配置指南 – 6.3. 配置图片存放目录
特别的,添加到 StandardHost 时,会直接调用 StandardContext 的 start() 方法来启动应用程序。启动应用程序步骤请看 Context Start 一节。另外,mysql 系列面试题和答案全部整理好了,微信搜索互联网架构师,在后台发送:2T,可以在线阅读。
Context Start
StandRoot 类实现了 WebResourceRoot 接口,它容纳了一个应用程序的所有资源,通俗的来说就是部署到 webapps 目录下对应 Context 的目录里的所有资源。因为我对 Tomcat 的资源管理部分暂时不是很感兴趣,所以资源管理相关类只是做了简单了解,并没有深入研究源代码。
resourceStart() 方法会对 StandardRoot 进行初始配置
postWorkDirectory() 用于创建对应的工作目录 $CATALINA_BASE/work///, 该目录用于存放临时文件。
StardardContext 只是一个容器,而 ApplicationContext 则是一个应用程序真正的运行环境,相关类及操作会在请求处理流程看完以后进行补充。
StardardContext 触发 CONFIGURE_START_EVENT 生命周期事件,ContextConfig 开始调用 configureStart() 对应用程序进行配置。
这个过程会解析并合并 conf/web.xml & conf///web.xml.default & webapps//WEB-INF/web.xml 中的配置。
配置配置文件中的参数到 StandardContext, 其中主要的包括 Servlet、Filter、Listener。
因为从 Servlet3.0 以后是直接支持注解的,所以服务器必须能够处理加了注解的类。Tomcat 通过分析 WEB-INF/classes/ 中的 Class 文件和 WEB-INF/lib/ 下的 jar 包将扫描到的 Servlet、Filter、Listerner 注册到 StandardContext。
setConfigured(true),是非常关键的一个操作,它标识了 Context 的成功配置,若未设置该值为 true 的话,Context 会启动失败。
Background process
后台进程的作用就是处理一下 Servlet 引擎中的周期性事件,处理周期默认是 10s。
特别的 StandardHost 的 backgroundProcess() 方法会触发 Host 的 PERIODIC_EVENT 生命周期事件。然后 HostConfig 会调用其 check() 方法对已加载并进行过重新部署的应用程序进行 reload 或对新部署的应用程序进行热部署。热部署跟之前介绍的部署步骤一致, reload() 过程只是简单的顺序调用 setPause(true)、stop()、start()、setPause(false),其中 setPause(true) 的作用是暂时停止接受请求。
How to read excellent open source projects
下面我简单分享一下我是如何阅读开源项目源码的。
先找一些介绍该项目架构的书籍来看,项目架构是项目核心中的核心,读架构读的是高层次的设计思路,读源码读的是低层次的实现细节。有了高层次的设计思路做指导,源码读起来才会得心应手,因为读的时候心里很清楚现在在读的源码在整个项目架构中处于什么位置。我在读 Tomcat 源码之前先把 《How Tomcat works》 一书过了一边,然后又看了一下 《Tomcat 架构解析》 的第二章,对 Tomcat 的架构有了初步了解。(PS:《How Tomcat works》一书是全英文的,但读起来非常流畅,虽然它是基于 Tomcat 4 和 5 的,但 Tomcat 架构没有非常大的变化,新版的 Tomcat 只是增加了一些组件,如果你要学习 Tomcat 的话,首推这本书!)
如果实在找不到讲架构的书,那就自己动手画类图吧!一般来说,开源项目都是为了提供服务的,我们把提供服务的流程作为主线来分析源代码,这样目的性会更强一些,将该流程中涉及到的类画到类图中,最后得到的类图就是架构!不过分析之前你要先找到流程的入口点,否则分析就无从开始。以 Tomcat 为例,他的主线流程大致可以分为 3 个:启动、部署、请求处理。他们的入口点就是 Bootstrap 类和 接受请求的 Acceptor 类!
有了阅读思路我们下面来说说工具吧。我使用的阅读工具是 IntelliJ IDEA,一款十分强大的 IDE,可能比较重量级,如果你有其他更加轻量级的 Linux 平台源码阅读工具,可以推荐给我~
Structure 栏目可以自定义列出类中的域、方法,然后还可以按照继承结构对域和方法进行分组,这样就可以直接看出来域和方法是在继承结构中哪个类里定义的。当你点击方法和域时,还可以自动滚动到源代码等等。
在源代码中 点击右键 -> Diagrams -> show Diagram 可以显示类的继承结构,图中包含了该类所有的祖先和所有的接口。在该图中选择指定的父类和接口,点击右键 -> show Implementations, IDEA 会列出接口的实现类或该类的子类。
FindUsage、Go To Declaration 等等就不再多说了。
Reference
https://www.amazon.com/How-Tomcat-Works-Budi-Kurniawan/dp/097521280X
http://product.dangdang.com/25084132.html
https://tomcat.apache.org/tomcat-9.0-doc/index.html
http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.0.M22/src/
-End-
最近有一些小伙伴,让我帮忙找一些 面试题 资料,于是我翻遍了收藏的 5T 资料后,汇总整理出来,可以说是程序员面试必备!所有资料都整理到网盘了,欢迎下载!
点击👆卡片,关注后回复【面试题
】即可获取
在看点这里好文分享给更多人↓↓
以上是关于面试官:谈谈 Tomcat 架构及启动过程,我一脸懵逼。。的主要内容,如果未能解决你的问题,请参考以下文章