Tomcat 核心原理分析,提升设计思维

Posted 里奥ii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat 核心原理分析,提升设计思维相关的知识,希望对你有一定的参考价值。


Tomcat 是一个免费的、开源的、轻量级的 Web 应用服务器。适合在并发量不是很高的中小企业项目中使用。学习本章,你将掌握它的目录结构、组件结构、连接器核心原理、容器核心原理、请求处理流程 和 SpringBoot 是如何启动内嵌Tomcat 这六大方面以及文章中出现的高频词汇。提升自己的设计思维,从容面对面试官的刁难问题。

目录结构

以下是 Tomcat 8 主要目录结构



Tomcat


组件结构

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



Tomcat




Tomcat


连接器核心原理

连接器核心功能

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

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

连接器模块设计

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



Tomcat


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



Tomcat


容器请求处理

容器的请求处理过程就是在 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 中找映射关系,最后调用具体的函数。



Tomcat


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);


源码分析流程图:



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



原作者:ITDragon博客



Tomcat


以上是关于Tomcat 核心原理分析,提升设计思维的主要内容,如果未能解决你的问题,请参考以下文章

了解架构设计远远不够!一文拆解 Tomcat 高并发原理与性能调优

技术提升计划「攀登技术领域的巅峰」教你学透MySQL技术原理及设计调优

大数据新兴思维

编程学习思维提升

人工智能来了,我们该如何提升创新思维?

大型网站技术架构:核心原理与案例分析阅读笔记二