分析Tomcat的工作原理

SpringBoot 就像一条巨蟒,慢慢缠绕着我们,使我们麻痹。不得不承认,使用了 SpringBoot 确实提高了工作效率,但同时也让我们遗忘了很多技能。刚入社会的时候,我还是通过 Tomcat 手动部署 JavaWeb 项目,还经常对 Tomcat 进行性能调优。除此之外,还需要自己理清楚各 Jar 之间的关系,以避免 Jar 丢失和各版本冲突导致服务启动异常的问题。到如今,这些繁琐而又重复的工作已经统统交给 SpringBoot 处理,我们可以把更多的精力放在业务逻辑上。但是,清楚 Tomcat 的工作原理和处理请求流程和分析 Spring 框架源码一样的重要。至少面试官特别喜欢问这些底层原理和设计思路。希望这篇文章能给你一些帮助。

Tomcat 整体架构

Tomcat 是一个免费的、开源的、轻量级的 Web 应用服务器。适合在并发量不是很高的中小企业项目中使用。

文件目录结构

以下是 Tomcat 8 主要目录结构

目录 功能说明
bin 存放可执行的文件,如 startup 和 shutdown
conf 存放配置文件,如核心配置文件 server.xml 和应用默认的部署描述文件 web.xml
lib 存放 Tomcat 运行需要的jar包
logs 存放运行的日志文件
webapps 存放默认的 web 应用部署目录
work 存放 web 应用代码生成和编译文件的临时目录

功能组件结构

Tomcat 的核心功能有两个,分别是负责接收和反馈外部请求的连接器 Connector,和负责处理请求的容器 Container。其中连接器和容器相辅相成,一起构成了基本的 web 服务 Service。每个 Tomcat 服务器可以管理多个 Service。

组件 功能
Connector 负责对外接收反馈请求。它是 Tomcat 与外界的交通枢纽,监听端口接收外界请求,并将请求处理后传递给容器做业务处理,最后将容器处理后的结果反馈给外界。
Container 负责对内处理业务逻辑。其内部由Engine、Host、Context 和 Wrapper 四个容器组成,用于管理和调用 Servlet 相关逻辑。
Service 对外提供的 Web 服务。主要包含连接器和容器两个核心组件,以及其他功能组件。Tomcat 可以管理多个 Service,且各 Service 之间相互独立。

Tomcat 连接器核心原理

Tomcat 连接器框架——Coyote

连接器核心功能

一、监听网络端口,接收和响应网络请求。

二、网络字节流处理。将收到的网络字节流转换成 Tomcat Request 再转成标准的 ServletRequest 给容器,同时将容器传来的 ServletResponse 转成 Tomcat Response 再转成网络字节流。

连接器模块设计

为满足连接器的两个核心功能,我们需要一个通讯端点来监听端口;需要一个处理器来处理网络字节流;最后还需要一个适配器将处理后的结果转成容器需要的结构。

组件 功能
Endpoint 端点,用来处理 Socket 接收和发送的逻辑。其内部由 Acceptor 监听请求、Handler 处理数据、AsyncTimeout 检查请求超时。具体的实现有 NioEndPoint、AprEndpoint 等。
Processor 处理器,负责构建 Tomcat Request 和 Response 对象。具体的实现有 Http11Processor、StreamProcessor 等。
Adapter 适配器,实现 Tomcat Request、Response 与 ServletRequest、ServletResponse之间的相互转换。这采用的是经典的适配器设计模式。
ProtocolHandler 协议处理器,将不同的协议和通讯方式组合封装成对应的协议处理器,如 Http11NioProtocol 封装的是 HTTP + NIO。

对应的源码包路径 org.apache.coyote 。对应的结构图如下

Tomcat 容器核心原理

Tomcat 容器框架——Catalina

容器结构分析

每个 Service 会包含一个容器。容器由一个引擎可以管理多个虚拟主机。每个虚拟主机可以管理多个 Web 应用。每个 Web 应用会有多个 Servlet 包装器。Engine、Host、Context 和 Wrapper,四个容器之间属于父子关系。

容器 功能
Engine 引擎,管理多个虚拟主机。
Host 虚拟主机,负责 Web 应用的部署。
Context Web 应用,包含多个 Servlet 封装器。
Wrapper 封装器,容器的最底层。对 Servlet 进行封装,负责实例的创建、执行和销毁功能。

对应的源码包路径 org.apache.coyote 。对应的结构图如下

容器请求处理

容器的请求处理过程就是在 Engine、Host、Context 和 Wrapper 这四个容器之间层层调用,最后在 Servlet 中执行对应的业务逻辑。各容器都会有一个通道 Pipeline,每个通道上都会有一个 Basic Valve(如StandardEngineValve), 类似一个闸门用来处理 Request 和 Response 。其流程图如下。

Tomcat 请求处理流程

上面的知识点已经零零碎碎地介绍了一个 Tomcat 是如何处理一个请求。简单理解就是连接器的处理流程 + 容器的处理流程 = Tomcat 处理流程。哈!那么问题来了,Tomcat 是如何通过请求路径找到对应的虚拟站点?是如何找到对应的 Servlet 呢?

映射器功能介绍

这里需要引入一个上面没有介绍的组件 Mapper。顾名思义,其作用是提供请求路径的路由映射。根据请求URL地址匹配是由哪个容器来处理。其中每个容器都会它自己对应的Mapper,如 MappedHost。不知道大家有没有回忆起被 Mapper class not found 支配的恐惧。在以前,每写一个完整的功能,都需要在 web.xml 配置映射规则,当文件越来越庞大的时候,各个问题随着也会出现

HTTP请求流程

打开 tomcat/conf 目录下的 server.xml 文件来分析一个http://localhost:8080/docs/api 请求。

第一步:连接器监听的端口是8080。由于请求的端口和监听的端口一致,连接器接受了该请求。

第二步:因为引擎的默认虚拟主机是 localhost,并且虚拟主机的目录是webapps。所以请求找到了 tomcat/webapps 目录。

第三步:解析的 docs 是 web 程序的应用名,也就是 context。此时请求继续从 webapps 目录下找 docs 目录。有的时候我们也会把应用名省略。

第四步:解析的 api 是具体的业务逻辑地址。此时需要从 docs/WEB-INF/web.xml 中找映射关系,最后调用具体的函数。

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">

 <Service name="Catalina">

	<!-- 连接器监听端口是 8080,默认通讯协议是 HTTP/1.1 -->
 <Connector port="8080" protocol="HTTP/1.1"
  connectionTimeout="20000"
  redirectPort="8443" />

	<!-- 名字为 Catalina 的引擎,其默认的虚拟主机是 localhost -->
 <Engine name="Catalina" defaultHost="localhost">

	 <!-- 名字为 localhost 的虚拟主机,其目录是 webapps-->
 <Host name="localhost" appBase="webapps"
  unpackWARs="true" autoDeploy="true">

 </Host>
 </Engine>
 </Service>
</Server>

SpringBoot 如何启动内嵌的 Tomcat

SpringBoot 一键启动服务的功能,让有很多刚入社会的朋友都忘记 Tomcat 是啥。随着硬件的性能越来越高,普通中小项目都可以直接用内置 Tomcat 启动。但是有些大一点的项目可能会用到 Tomcat 集群和调优,内置的 Tomcat 就不一定能满足需求了。

我们先从源码中分析 SpringBoot 是如何启动 Tomcat,以下是 SpringBoot 2.x 的代码。

代码从 main 方法开始,执行 run 方法启动项目。

SpringApplication.run

从 run 方法点进去,找到刷新应用上下文的方法。

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);

从 refreshContext 方法点进去,找 refresh 方法。并一层层往上找其父类的方法。

this.refresh(context);

在 AbstractApplicationContext 类的 refresh 方法中,有一行调用子容器刷新的逻辑。

this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();

从 onRefresh 方法点进去,找到 ServletWebServerApplicationContext 的实现方法。在这里终于看到了希望。

protected void onRefresh() {
 super.onRefresh();

 try {
 this.createWebServer();
 } catch (Throwable var2) {
 throw new ApplicationContextException("Unable to start web server", var2);
 }
}

从 createWebServer 方法点进去,找到从工厂类中获取 WebServer的代码。

if (webServer == null && servletContext == null) {
 ServletWebServerFactory factory = this.getWebServerFactory();
 // 获取 web server
 this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
} else if (servletContext != null) {
 try {
 // 启动 web server
 this.getSelfInitializer().onStartup(servletContext);
 } catch (ServletException var4) {
 throw new ApplicationContextException("Cannot initialize servlet context", var4);
 }
}

从 getWebServer 方法点进去,找到 TomcatServletWebServerFactory 的实现方法,与之对应的还有 Jetty 和 Undertow。这里配置了基本的连接器、引擎、虚拟站点等配置。

public WebServer getWebServer(ServletContextInitializer... initializers) {
 Tomcat tomcat = new Tomcat();
 File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
 tomcat.setBaseDir(baseDir.getAbsolutePath());
 Connector connector = new Connector(this.protocol);
 tomcat.getService().addConnector(connector);
 this.customizeConnector(connector);
 tomcat.setConnector(connector);
 tomcat.getHost().setAutoDeploy(false);
 this.configureEngine(tomcat.getEngine());
 Iterator var5 = this.additionalTomcatConnectors.iterator();

 while(var5.hasNext()) {
 Connector additionalConnector = (Connector)var5.next();
 tomcat.getService().addConnector(additionalConnector);
 }

 this.prepareContext(tomcat.getHost(), initializers);
 return this.getTomcatWebServer(tomcat);
}

服务启动后会打印日志

o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8900 (http)
o.apache.catalina.core.StandardService : Starting service [Tomcat]
org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.34
o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal ...
o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 16858 ms

END

文章到这里就结束了,实在是 hold 不住了,周末写了一整天还没有写到源码部分,只能放在下一章了。再写真的就要废了,有什么不对的地方请多多指出

以上就是Tomcat的工作原理是怎样的的详细内容,更多关于Tomcat 工作原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • tomcat中Servlet的工作机制详细介绍

    tomcat中Servlet的工作机制 在研究Servlet在tomcat中的工作机制前必须先看看Servlet规范的一些重要的相关规定,规范提供了一个Servlet接口,接口中包含的重要方法是init.service.destroy等方法,Servlet在初始化时要调用init方法,在销毁时要调用destroy方法,而对客户端请求处理时则调用service方法.对于这些机制的支持都必须由Tomcat内部去支持,具体则是由Wrapper容器提供支持. 在tomcat中消息流的流转机制是通过四个不

  • Tomcat解析XML和反射创建对象原理

    下面通过实例代码给大家介绍Tomcat解析XML和反射创建对象原理,具体代码如下所示: import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import or

  • Tomcat中的startup.bat原理详细解析

    前言 在刚开始接触计算机,一开始就是win2000,所以对批处理脚本命令都不会.平时启TOMCAT都是鼠标双击startup.bat了,很少看过里面写的是什么,也借学习TOMCAT的机会学习一下批处理的常用命令,不求都记住,但求以后再见到批处理命令能看的懂,说的出是干什么的.本文主要给大家介绍了关于Tomcat中startup.bat原理的相关内容,下面话不多说了,来一起看看详细的介绍吧. startup.bat 解析 验证CATALINA_HOME 环境变量是否设置,如果没有设置则通过CATA

  • tomcat设置gzip压缩的原理及配置方法

    原理简介 HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求服务器对应资源后,从服务器端将资源文件压缩,再输出到客户端,由客户端的浏览器负责解压缩并浏览.相对于普通的浏览过程HTML ,CSS,Javascript , Text ,它可以节省40%左右的流量.更为重要的是,它可以对动态生成的,包括CGI.PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,压缩效率也很高. 配置方法 Tomcat5.0以后的版本是支持对输出内容进行压缩的,使用的

  • Tomcat中的catalina.bat原理详细解析

    前言 本文主要给大家详细解析了关于Tomcat中catalina.bat原理的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. tomcat 的真正启动是在 catalina.bat 设置并启动的.startup.bat 只是找到catalina.bat 然后执行catalina.bat 来启动tomat的.下面我们来分析下catalina.bat 验证CATALINA_HOME 环境变量 验证CATALINA_HOME 设置是否正确,如果不正确,重新设置CATALIN

  • Tomcat 热部署的实现原理详解

    Tomcat热部署机制 对于Java应用程序来说,热部署就是在运行时更新Java类文件.在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色.大多数基于Java的应用服务器,包括EJB服务器和Servlet容器,都支持热部署.类装入器不能重新装入一个已经装入的类,但只要使用一个新的类装入器实例,就可以将类再次装入一个正在运行的应用程序. 我们知道,现在大多数的web服务器都支持热部署,而对于热部署的实现机制,网上讲的却不够完善,下面我们就Tomcat的热部署实现机制,讲解一下它

  • 基于Tomcat 数据源的原理、配置、使用介绍

    1.数据源的作用及操作原理 在程序代码中使用数据源是可以提升操作性能的,这种性能的提升依靠于运行的原理. 传统JDBC操作步骤 1.加载数据库驱动程序,数据库驱动程序通过CLASSPATH配置: 2.通过DriverManager类取得数据库连接对象: 3.通过Connection实例化PreparedStatement对象,编写SQL命令操作数据库: 4.数据库属于资源操作,操作完成后进行数据库的关闭以释放资源.如图所示: 对于不同的用户只有操作不同,但是对于1.2.4三个步骤很明显是一个重复

  • 分析Tomcat的工作原理

    SpringBoot 就像一条巨蟒,慢慢缠绕着我们,使我们麻痹.不得不承认,使用了 SpringBoot 确实提高了工作效率,但同时也让我们遗忘了很多技能.刚入社会的时候,我还是通过 Tomcat 手动部署 JavaWeb 项目,还经常对 Tomcat 进行性能调优.除此之外,还需要自己理清楚各 Jar 之间的关系,以避免 Jar 丢失和各版本冲突导致服务启动异常的问题.到如今,这些繁琐而又重复的工作已经统统交给 SpringBoot 处理,我们可以把更多的精力放在业务逻辑上.但是,清楚 Tom

  • 代码分析Spring MVC的工作原理

    遗留问题 在关于利用maven搭建ssm的博客,我们一起来探讨下问的最多的问题中,我遗留了一个问题:Spring mvc是何时.何地.如何将Model中的属性绑定到哪个作用域,这里的作用域指的是Servlet的四大作用域:不了解问题背景的可以回过头去看看我的上篇博文. 明确的解答我会放到最后,在解答问题之前,我先和大家一起来捋一捋Spring mvc的工作原理.废话不多说,开始我们神秘的探险之旅! 应用示例 在讲工作原理之前,我们先看一个简单的spring mvc(ssm)示例,以及实现的效果

  • Yii中srbac权限扩展模块工作原理与用法分析

    本文实例讲述了Yii中srbac权限扩展模块工作原理与用法.分享给大家供大家参考,具体如下: 1. 设置权限规则表:可放在module模块配置文件里面 public function init() { //操作权限表,必须存在以下字段: //itemname角色名/ID, //type授权项目类型/1(任务)或者2(角色), //bizrule权限/逻辑运算表达式为false是有权限操作, //data数据/YII暂无利用 Yii::app()->authManager->itemTable

  • 深入解析Session工作原理及运行流程

    一.session的概念及特点 session概念:在计算机中,尤其是在网络应用中,称为"会话控制".Session 对象存储特定用户会话所需的属性及配置信息.说白了session就是一种可以维持服务器端的数据存储技术.session主要有以下的这些特点: session保存的位置是在服务端 session一般来说要配合cookie使用,如果用户浏览器禁用了cookie,那么只能使用URL重写来实现session的存储功能 单纯的使用session来存储用户回话信息,那么当用户量较多时

  • 分析SpringBoot的启动原理

    背景 1> 大家都知道SpringBoot是通过main函数启动的,这里面跟踪代码到处都没有找到while(true),为什么启动后可以一直跑? 2> SpringBoot默认使用tomcat作为web容器.大家也可以通过在pom文件中exclusion掉tomcat,denpendency jetty 的方法来使用jetty.那SpringBoot是怎么做到在不同web容器之间切换的呢? 3> 传统的web容器比如jetty本质上是直接通过java start.jar 来启动,之后来加

  • DDNS 的工作原理及其在 Linux 上的实现

    DDNS 工作原理的分析 DDNS 的实现最根本的一点是当主机的 IP 地址发生变化的时候,实现 DNS 映射信息的及时更新,应用程序需要及时地获得这一信息,主要的方法可分为两大类: 一类是轮询机制,即:应用程序每隔一定的时间,去从查询主机当前的 IP 地址,并与之前的进行比较,从而判断网络地址是否发生了变化.显然,这种方法不仅效率低下,而且对每次查询 IP 地址的时间间隔很难得到一个折中的数值. 第二类方法是异步实现方式,即:每当主机的 IP 地址发生变化的时候,应用程序能够被及时地通知到.这

  • 深度剖析Java中的内存原型及工作原理

    本文主要通过分析Java内存分配的栈.堆以以及常量池详细的讲解了其的工作原理. 一.java虚拟机内存原型 寄存器:我们在程序中无法控制栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中堆:存放用new产生的数据静态域:存放在对象中用static定义的静态成员常量池:存放常量非RAM存储:硬盘等永久存储空间. 二.常量池(constant pool) 常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据.除了包含代码中所定义的各种基本类型(如int.

  • servlet之session工作原理简介_动力节点Java学院整理

    要了解Session的底层工作原理.我们还是先看在一个会话过程中,同一个浏览器在访问多个web资源的情况好了,大致分为以下几个步骤: 1,浏览器访问某个Servlet,这时如果服务器要从请求对象中获取Session对象(第一次获取也是创建),那么服务器会为这个Session对象创建一个id:JSESSIONID 2,同时在对浏览器的响应过程中,这个Session会将JSESSIONID这个id以Cookie形式回送给客户端浏览器,记住,这时候Cookie服务器没有设置有效时间,因此是存在浏览器的

  • 浅析ASP.NET路由模型工作原理

    ps:这是针对ASP.NET4.5版本的,好像在最新的5.0版本中加入了OWIN,彻底解耦了和Web服务器的耦合,我还没有研究过,不敢妄言4.5的模型适用5.0. action*0x1:大话ASP.NET模型 首先我们先来了解下一个请求的悲欢离合的命运,看看它的一生中所走过的蜿蜒曲折的道路.如下图所示: 在如上所示的风光旖旎的画卷中,我们可以看到一个"请求"从客户端浏览器出发,经历千山万水到达服务器,服务器的内核模块的HTTP.SYS热情款待了它,对它进行简单的修饰之后,就和它依依惜别

  • Ajax工作原理深入理解

    1.ajax技术的背景 不可否认,ajax技术的流行得益于google的大力推广,正是由于google earth.google suggest以及gmail等对ajax技术的广泛应用,催生了ajax的流行.而这也让微软感到无比的尴尬,因为早在97年,微软便已经发明了ajax中的关键技术,并且在99年IE5推出之时,它便开始支持XmlHttpRequest对象,并且微软之前已经开始在它的一些产品中应用ajax,比如说MSDN网站菜单中的一些应用.遗憾的是,不知道出于什么想法,当时微软发明了aja

随机推荐