tomcat的工作原理
本文源自轉載:你還記得 Tomcat 的工作原理么
一、Tomcat 整體架構
Tomcat 是一個免費的、開源的、輕量級的 Web 應用服務器。適合在并發量不是很高的中小企業項目中使用。
?
二、文件目錄結構
以下是 Tomcat 8 主要目錄結構
?
三、功能組件結構
Tomcat 的核心功能有兩個,分別是負責接收和反饋外部請求的連接器 Connector,和負責處理請求的容器 Container。其中連接器和容器相輔相成,一起構成了基本的 web 服務 Service。每個 Tomcat 服務器可以管理多個 Service。
?
四、Tomcat 連接器核心原理
Tomcat 連接器框架——Coyote
4.1 連接器核心功能
一、監聽網絡端口,接收和響應網絡請求。
二、網絡字節流處理。將收到的網絡字節流轉換成 Tomcat Request 再轉成標準的 ServletRequest 給容器,同時將容器傳來的 ServletResponse 轉成 Tomcat Response 再轉成網絡字節流。
4.2 連接器模塊設計
為滿足連接器的兩個核心功能,我們需要一個通訊端點來監聽端口;需要一個處理器來處理網絡字節流;最后還需要一個適配器將處理后的結果轉成容器需要的結構。
對應的源碼包路徑?org.apache.coyote?。對應的結構圖如:
五、Tomcat 容器核心原理
Tomcat 容器框架——Catalina
5.1 容器結構分析
每個 Service 會包含一個容器。容器由一個引擎可以管理多個虛擬主機。每個虛擬主機可以管理多個 Web 應用。每個 Web 應用會有多個 Servlet 包裝器。Engine、Host、Context 和 Wrapper,四個容器之間屬于父子關系。
對應的源碼包路徑?org.apache.coyote?。對應的結構圖如下:
5.2 容器請求處理
容器的請求處理過程就是在 Engine、Host、Context 和 Wrapper 這四個容器之間層層調用,最后在 Servlet 中執行對應的業務邏輯。各容器都會有一個通道 Pipeline,每個通道上都會有一個 Basic Valve(如StandardEngineValve), 類似一個閘門用來處理 Request 和 Response 。其流程圖如下。
六、Tomcat 請求處理流程
上面的知識點已經零零碎碎地介紹了一個 Tomcat 是如何處理一個請求。簡單理解就是連接器的處理流程 + 容器的處理流程 = Tomcat 處理流程。哈!那么問題來了,Tomcat 是如何通過請求路徑找到對應的虛擬站點?是如何找到對應的 Servlet 呢?
6.1 映射器功能介紹
這里需要引入一個上面沒有介紹的組件 Mapper。顧名思義,其作用是提供請求路徑的路由映射。根據請求URL地址匹配是由哪個容器來處理。其中每個容器都會它自己對應的Mapper,如 MappedHost。不知道大家有沒有回憶起被 Mapper class not found 支配的恐懼。在以前,每寫一個完整的功能,都需要在 web.xml 配置映射規則,當文件越來越龐大的時候,各個問題隨著也會出現
6.2 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 serverthis.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?
總結
以上是生活随笔為你收集整理的tomcat的工作原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 同批号不同批次同一单据中出现数量不限制
- 下一篇: [密码学基础][每个信息安全博士生应该知