本文主要介绍Tomcat的工作原理,帮助您了解Tomcat的工作原理以及处理请求的过程和分析。感兴趣的朋友可以了解一下。
跳羚就像一条巨蟒,慢慢缠绕我们,麻痹我们。不得不承认,SpringBoot的使用确实提高了工作效率,但同时也让我们忘记了很多技巧。刚入社的时候还是通过Tomcat手动部署JavaWeb项目,经常调Tomcat的性能。另外,你需要自己整理好JAR之间的关系,避免JAR丢失和版本冲突导致的服务启动异常的问题。到现在,所有这些繁琐重复的工作都交给了SpringBoot,我们可以更专注于业务逻辑。但是,了解Tomcat的工作原理和处理请求的方式和分析Spring框架的源代码一样重要。至少面试官喜欢问这些底层原理和设计思路。希望这篇文章能给你一些帮助。
Tomcat的整体架构
Tomcat是一个免费的、开源的轻量级Web应用服务器。适用于低并发的中小型企业项目。
文件目录结构
下面是Tomcat8的主目录结构
功能组件结构
Tomcat有两个核心功能,连接器Connector,负责接收和反馈外部请求,容器Container,负责处理请求。其中,连接器和容器相辅相成,共同构成了基本的web服务。每个Tomcat服务器可以管理多个服务。
Tomcat连接器核心原理
Tomcat连接器框架-coyote
连接器的核心功能
1.监控网络端口以接收和响应网络请求。
第二,网络字节流处理。接收到的网络字节流转换成Tomcat请求,然后转换成标准的ServletRequest给容器,来自容器的ServletResponse转换成Tomcat响应,然后转换成网络字节流。
连接器模块的设计
为了满足连接器的两个核心功能,我们需要一个通信端点来监控端口;需要处理器来处理网络字节流;最后,需要一个适配器将处理后的结果转换成容器所需的结构。
org.apache.coyote对应的源码包路径对应的结构图如下
Tomcat容器核心原则
Tomcat容器框架-catalina
容器结构分析
每个服务将包含一个容器。一个容器可以通过一个引擎管理多个虚拟主机。每个虚拟主机可以管理多个Web应用程序。每个Web应用程序都有多个Servlet包装器。引擎、主机、上下文和包装器,属于父子关系。
org.apache.coyote对应的源码包路径对应的结构图如下
容器请求处理
容器的请求处理过程是一层一层的调用四个容器,引擎、主机、上下文、包装器,最后在Servlet中执行相应的业务逻辑。每个容器都会有一个通道管道,每个通道都会有一个基本的阀门(比如StandardEngineValve),类似于处理请求和响应的闸门。流程图如下。
Tomcat请求处理流程
以上知识点已经零零碎碎的介绍了Tomcat如何处理请求。简单理解就是连接器的处理流程+容器的处理流程=Tomcat处理流程。哈!那么问题来了,Tomcat是怎么通过请求路径找到对应的虚拟站点的呢?如何找到对应的Servlet?
映射器函数介绍
需要引入上面没有介绍的组件映射器。顾名思义,它的功能是提供请求路径的路由映射。根据哪个容器来处理请求URL地址匹配。每个容器都有自己的映射器,比如MappedHost。不知道大家有没有回忆过被Mapper类支配不被发现的恐惧。以前每次编写一个完整的函数,都需要在web.xml中配置映射规则,当文件越来越大的时候,各种问题也出现了。
HTTP请求过程
打开tomcat/conf目录中的server.xml文件来分析http://localhost:8080/docs/api请求。
步骤1:连接器侦听的端口是8080。连接器接受了请求,因为请求的端口与被监视的端口相同。
第二步:因为引擎默认的虚拟主机是localhost,虚拟主机的目录是webapps。于是请求找到了tomcat/webapps目录。
第三步:解析出的docs是web程序的应用名,也就是context。此时,请求继续从webapps目录中查找docs目录。有时我们也会省略应用程序名。
步骤4:解析的api是特定的业务逻辑地址。此时,你需要从docs/WEB-INF/web.xml中找到映射关系,最后调用具体的函数。
<?xmlversion="1.0"encoding="UTF-8"?> <Serverport="8005"shutdown="SHUTDOWN"> <Servicename="Catalina"> <!--连接器监听端口是8080,默认通讯协议是HTTP/1.1--> <Connectorport="8080"protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/> <!--名字为Catalina的引擎,其默认的虚拟主机是localhost--> <Enginename="Catalina"defaultHost="localhost"> <!--名字为localhost的虚拟主机,其目录是webapps--> <Hostname="localhost"appBase="webapps" unpackWARs="true"autoDeploy="true"> </Host> </Engine> </Service> </Server>SpringBoot如何开始嵌入式Tomcat
SpringBoot一键启动服务功能,让很多刚步入社会的朋友忘记了什么是Tomcat。随着硬件性能的不断提升,普通中小型项目可以直接用内置的Tomcat启动。但是,一些较大的项目可能会使用Tomcat集群和调优,内置的Tomcat可能无法满足需求。
我们先从源代码来分析SpringBoot是如何启动Tomcat的。以下是SpringBoot2.x的代码
代码从main方法开始,并执行run方法来启动项目。
SpringApplication.run从运行方法的角度来看,找到一种刷新应用程序上下文的方法。
this.prepareContext(context,environment,listeners,applicationArguments,printedBanner); this.refreshContext(context); this.afterRefresh(context,applicationArguments);单击refreshContext方法以查找刷新方法。并逐层寻找其父类。
this.refresh(context);在AbstractApplicationContext类的refresh方法中,有一行逻辑调用子容器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的实现方法。这里终于看到了希望。
protectedvoidonRefresh(){ super.onRefresh(); try{ this.createWebServer(); }catch(Throwablevar2){ thrownewApplicationContextException("Unabletostartwebserver",var2); } }单击createWebServer方法,找到从工厂类获取WebServer的代码。
if(webServer==null&&servletContext==null){ ServletWebServerFactoryfactory=this.getWebServerFactory(); //获取webserver this.webServer=factory.getWebServer(newServletContextInitializer[]{this.getSelfInitializer()}); }elseif(servletContext!=null){ try{ //启动webserver this.getSelfInitializer().onStartup(servletContext); }catch(ServletExceptionvar4){ thrownewApplicationContextException("Cannotinitializeservletcontext",var4); } }点击getWebServer方法,找到TomcatServletWebServerFactory的实现方法,对应Jetty和Undertow。下面是连接器、引擎、虚拟站点等的基本配置。
publicWebServergetWebServer(ServletContextInitializer...initializers){ Tomcattomcat=newTomcat(); FilebaseDir=this.baseDirectory!=null?this.baseDirectory:this.createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connectorconnector=newConnector(this.protocol); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iteratorvar5=this.additionalTomcatConnectors.iterator(); while(var5.hasNext()){ ConnectoradditionalConnector=(Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } this.prepareContext(tomcat.getHost(),initializers); returnthis.getTomcatWebServer(tomcat); }服务启动后将打印日志。
o.s.b.w.embedded.Tomcat.Tomcatwebserver:用端口初始化的Tomcat:8900(http)
o.Apache.catalina.core.standardservice:启动服务[Tomcat]
org.Apache.catalina.core.standardEngine:启动Servlet引擎:ApacheTomcat/8.5.34
o.a.catalina.core.aprlivecyclelistene...
o.a.c.c.C.[Tomcat]。[本地主机]。[/]:正在初始化SpringembeddedWebApplicationContext
o.s.web.context.contextloader:RootWebApplicationContext:初始化在16858毫秒内完成
END
文章到此结束。我再也憋不住了。周末一整天都没写源码,只好放到下一章了。照片又要报废了。请指出任何错误。
这就是Tomcat工作的细节。关于Tomcat如何工作的更多信息,请关注我们的其他相关文章!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)