摘要:本篇文章主要是跟大家聊聊的內(nèi)部架構(gòu)體系,讓大家對(duì)有個(gè)整體的認(rèn)知。方法會(huì)創(chuàng)建一個(gè)對(duì)象,調(diào)用它的方法將字節(jié)流封裝成對(duì)象,在創(chuàng)建組件時(shí),會(huì)將組件添加到組件中組件而組件在連接器初始化時(shí)就已經(jīng)創(chuàng)建好了目前為止,只有一個(gè)實(shí)現(xiàn)類,就是。
微信公眾號(hào)「后端進(jìn)階」,專注后端技術(shù)分享:Java、Golang、WEB框架、分布式中間件、服務(wù)治理等等。
老司機(jī)傾囊相授,帶你一路進(jìn)階,來(lái)不及解釋了快上車!
Tomcat 是 Java WEB 開(kāi)發(fā)接觸最多的 Servlet 容器,但它不僅僅是一個(gè) Servlet 容器,它還是一個(gè) WEB 應(yīng)用服務(wù)器,在微服務(wù)架構(gòu)體系下,為了降低部署成本,減少資源的開(kāi)銷,追求的是輕量化與穩(wěn)定,而 Tomcat 是一個(gè)輕量級(jí)應(yīng)用服務(wù)器,自然被很多開(kāi)發(fā)人員所接受。
Tomcat 里面藏著很多值得我們每個(gè) Java WEB 開(kāi)發(fā)者學(xué)習(xí)的知識(shí),可以這么說(shuō),當(dāng)你弄懂了 Tomcat 的設(shè)計(jì)原理,Java WEB 開(kāi)發(fā)對(duì)你來(lái)說(shuō)已經(jīng)沒(méi)有什么秘密可言了。本篇文章主要是跟大家聊聊 Tomcat 的內(nèi)部架構(gòu)體系,讓大家對(duì) Tomcat 有個(gè)整體的認(rèn)知。
前面我也說(shuō)了,Tomcat 的本質(zhì)其實(shí)就是一個(gè) WEB 服務(wù)器 + 一個(gè) Servlet 容器,那么它必然需要處理網(wǎng)絡(luò)的連接與 Servlet 的管理,因此,Tomcat 設(shè)計(jì)了兩個(gè)核心組件來(lái)實(shí)現(xiàn)這兩個(gè)功能,分別是連接器和容器,連接器用來(lái)處理外部網(wǎng)絡(luò)連接,容器用來(lái)處理內(nèi)部 Servlet,我用一張圖來(lái)表示它們的關(guān)系:
一個(gè) Tomcat 代表一個(gè) Server 服務(wù)器,一個(gè) Server 服務(wù)器可以包含多個(gè) Service 服務(wù),Tomcat 默認(rèn)的 Service 服務(wù)是 Catalina,而一個(gè) Service 服務(wù)可以包含多個(gè)連接器,因?yàn)?Tomcat 支持多種網(wǎng)絡(luò)協(xié)議,包括 HTTP/1.1、HTTP/2、AJP 等等,一個(gè) Service 服務(wù)還會(huì)包括一個(gè)容器,容器外部會(huì)有一層 Engine 引擎所包裹,負(fù)責(zé)與處理連接器的請(qǐng)求與響應(yīng),連接器與容器之間通過(guò) ServletRequest 和 ServletResponse 對(duì)象進(jìn)行交流。
也可以從 server.xml 的配置結(jié)構(gòu)可以看出 tomcat 整體的內(nèi)部結(jié)構(gòu):
連接器(Connector)
連接器負(fù)責(zé)將各種網(wǎng)絡(luò)協(xié)議封裝起來(lái),對(duì)外部屏蔽了網(wǎng)絡(luò)連接與 IO 處理的細(xì)節(jié),將處理得到的 Request 對(duì)象傳遞給容器處理,Tomcat 將處理請(qǐng)求的細(xì)節(jié)封裝到 ProtocolHandler,ProtocolHandler 是一個(gè)接口類型,通過(guò)實(shí)現(xiàn) ProtocolHandler 來(lái)實(shí)現(xiàn)各種協(xié)議的處理,如 Http11AprProtocol:
ProtocolHandler 采用組件模式的設(shè)計(jì),將處理網(wǎng)絡(luò)連接,字節(jié)流封裝成 Request 對(duì)象,再將 Request 適配成 Servlet 處理 ServletRequest 對(duì)象這幾個(gè)動(dòng)作,用組件封裝起來(lái)了,ProtocolHandler 包括了三個(gè)組件:Endpoint、Processor、Adapter。
Endpoint 在 ProtocolHandler 實(shí)現(xiàn)類的構(gòu)造方法中創(chuàng)建,如下:
public Http11AprProtocol() { super(new AprEndpoint()); }
Endpoint 組件用來(lái)處理底層的 Socket 網(wǎng)絡(luò)連接,AprEndpoint 里面有個(gè)叫 SocketProcessor 的內(nèi)部類,它負(fù)責(zé)為 AprEndpoint 將接收到的 Socket 請(qǐng)求轉(zhuǎn)化成 Request 對(duì)象,SocketProcessor 實(shí)現(xiàn)了 Runnable 接口,它會(huì)有一個(gè)專門(mén)的線程池來(lái)處理,后面我會(huì)多帶帶從源碼的角度分析 Endpoint 組件的設(shè)計(jì)原理。
org.apache.tomcat.util.net.AprEndpoint.SocketProcessor#doRun:
// Process the request from this socket SocketState state = getHandler().process(socketWrapper, event);
process 方法會(huì)創(chuàng)建一個(gè) processor 對(duì)象,調(diào)用它的 process 方法將 Socket 字節(jié)流封裝成 Request 對(duì)象,在創(chuàng)建 Processor 組件時(shí),會(huì)將 Adapter 組件添加到 Processor 組件中:
org.apache.coyote.http11.AbstractHttp11Protocol#createProcessor:
protected Processor createProcessor() { Http11Processor processor = new Http11Processor(); // set Adapter 組件 processor.setAdapter(getAdapter()); return processor; }
而 Adapter 組件在連接器初始化時(shí)就已經(jīng)創(chuàng)建好了:
org.apache.catalina.connector.Connector#initInternal:
// Initialize adapter adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter);
目前為止,Tomcat 只有一個(gè) Adapter 實(shí)現(xiàn)類,就是 CoyoteAdapter。Adapter 的主要作用是將 Request 對(duì)象適配成容器能夠識(shí)別的 Request 對(duì)象,比如 Servlet 容器,它的只能識(shí)別 ServletRequest 對(duì)象,這時(shí)候就需要 Adapter 適配器類作一層適配。
以上連接器的各個(gè)組件,我用一張圖說(shuō)明它們直接的關(guān)系:
容器(Container)在 Tomcat 中一共設(shè)計(jì)了 4 種容器,它們分別為 Engine、Host、Context、Wrapper,它們的關(guān)系如下圖所示:
Engine:表示一個(gè)虛擬主機(jī)的引擎,一個(gè) Tomcat Server 只有一個(gè) 引擎,連接器所有的請(qǐng)求都交給引擎處理,而引擎則會(huì)交給相應(yīng)的虛擬主機(jī)去處理請(qǐng)求;
Host:表示虛擬主機(jī),一個(gè)容器可以有多個(gè)虛擬主機(jī),每個(gè)主機(jī)都有對(duì)應(yīng)的域名,在 Tomcat 中,一個(gè) webapps 就代表一個(gè)虛擬主機(jī),當(dāng)然 webapps 可以配置多個(gè);
Context:表示一個(gè)應(yīng)用容器,一個(gè)虛擬主機(jī)可以擁有多個(gè)應(yīng)用,webapps 中每個(gè)目錄都代表一個(gè) Context,每個(gè)應(yīng)用可以配置多個(gè) Servlet。
從上圖可看出,各個(gè)容器組件之間的關(guān)系是由大到小,即父子關(guān)系,它們之間關(guān)系形成一個(gè)樹(shù)狀的結(jié)構(gòu),它們的實(shí)現(xiàn)類都實(shí)現(xiàn)了 Container 接口,它有如下方法來(lái)控制容器組件之間的關(guān)系:
public interface Container extends Lifecycle { Container getParent(); void setParent(Container container); void addChild(Container child); Container findChild(String name); Container[] findChildren(); void removeChild(Container child); }
容器組件之間通過(guò)以上幾個(gè)方法,即可實(shí)現(xiàn)它們之間的父子關(guān)系,有沒(méi)有發(fā)現(xiàn),Container 接口還繼承了 Lifecycle 接口,它有如下方法:
public interface Lifecycle { public static final String INIT_EVENT = "init"; public static final String START_EVENT = "start"; public static final String BEFORE_START_EVENT = "before_start"; public static final String AFTER_START_EVENT = "after_start"; public static final String STOP_EVENT = "stop"; public static final String BEFORE_STOP_EVENT = "before_stop"; public static final String AFTER_STOP_EVENT = "after_stop"; public static final String DESTROY_EVENT = "destroy"; public void addLifecycleListener(LifecycleListener listener); public LifecycleListener[] findLifecycleListeners(); public void removeLifecycleListener(LifecycleListener listener); public void start() throws LifecycleException; public void stop() throws LifecycleException; }
Tomcat 中有很多組件,組件通過(guò)實(shí)現(xiàn) Lifecycle 接口,Tomcat 通過(guò)事件機(jī)制來(lái)實(shí)現(xiàn)對(duì)這些組件生命周期的管理。
Tomcat 的這種容器設(shè)計(jì)思想,其實(shí)是運(yùn)用了組合設(shè)計(jì)模式的思想,組合設(shè)計(jì)模式最大的優(yōu)點(diǎn)是可以自由添加節(jié)點(diǎn),這樣也就使得 Tomcat 的容器組件非常地容易進(jìn)行擴(kuò)展,符合設(shè)計(jì)模式中的開(kāi)閉原則。
現(xiàn)在我們知道了 Tomcat 的容器組件的組合方式,那我們現(xiàn)在就來(lái)想一個(gè)問(wèn)題:
當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)時(shí),Tomcat 是如何識(shí)別請(qǐng)求并將它交給特定 Servlet 來(lái)處理呢?
從容器的組合關(guān)系可以看出,它們調(diào)用順序必定是:
Engine -> Host -> Context -> Wrapper -> Servlet
那么 Tomcat 是如何來(lái)定位 Servlet 的呢?答案是利用 Mapper 組件來(lái)完成定位的工作。
Mapper 最主要的核心功能是保存容器組件之間訪問(wèn)路徑的映射關(guān)系,它是如何做到這點(diǎn)的呢?
我們不妨先從源碼入手:
org.apache.catalina.core.StandardService:
protected final Mapper mapper = new Mapper(); protected final MapperListener mapperListener = new MapperListener(this);
Service 實(shí)現(xiàn)類中,已經(jīng)初始化了 Mapper 組件以及它的監(jiān)聽(tīng)類 MapperListener,這里先說(shuō)明一下,在 Tomcat 組件中,標(biāo)準(zhǔn)的實(shí)現(xiàn)組件類前綴會(huì)有 Standard,比如:
org.apache.catalina.core.StandardServer org.apache.catalina.core.StandardService org.apache.catalina.core.StandardEngine org.apache.catalina.core.StandardHost org.apache.catalina.core.StandardContext org.apache.catalina.core.StandardWrapperz
在 Service 服務(wù)啟動(dòng)的時(shí)候,會(huì)調(diào)用 MapperListener.start() 方法,最終會(huì)執(zhí)行 MapperListener 的 startInternal 方法:
org.apache.catalina.mapper.MapperListener#startInternal:
Container[] conHosts = engine.findChildren(); for (Container conHost : conHosts) { Host host = (Host) conHost; if (!LifecycleState.NEW.equals(host.getState())) { // Registering the host will register the context and wrappers registerHost(host); } }
該方法會(huì)注冊(cè)新的虛擬主機(jī),接著 registerHost() 方法會(huì)注冊(cè) context,以此類推,從而將容器組件直接的訪問(wèn)的路徑都注冊(cè)到 Mapper 中。
定位 Servlet 的流程圖:
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/74716.html
摘要:相信大家都聽(tīng)說(shuō)過(guò)反向代理,一提到反向代理一定會(huì)想到。由于是一款自由的開(kāi)源的高性能的服務(wù)器和反向代理服務(wù)器這是個(gè)開(kāi)源的時(shí)代啊是一個(gè)跨平臺(tái)服務(wù)器,可以運(yùn)行在等操作系統(tǒng)上。所以反向代理服務(wù)器是引用在服務(wù)端。 本文來(lái)自于我的慕課網(wǎng)手記:聊聊 Nginx 的反向代理,轉(zhuǎn)載請(qǐng)保留鏈接 ;) 背景 最近在優(yōu)化服務(wù)基礎(chǔ)設(shè)施這塊,正好有時(shí)間寫(xiě)一下Nginx的體會(huì)。相信大家都聽(tīng)說(shuō)過(guò)反向代理,一提到反向代理...
摘要:英文全名為,也叫遠(yuǎn)程過(guò)程調(diào)用,其實(shí)就是一個(gè)計(jì)算機(jī)通信協(xié)議,它是一種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù)而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。 Hello,Dubbo 你好,dubbo,初次見(jiàn)面,我想和你交個(gè)朋友。 Dubbo你到底是什么? 先給出一套官方的說(shuō)法:Apache Dubbo是一款高性能、輕量級(jí)基于Java的RPC開(kāi)源框架。 那么什么是RPC? 文檔地址:http://dubbo.a...
閱讀 1887·2021-11-11 16:55
閱讀 2088·2021-10-08 10:13
閱讀 750·2019-08-30 11:01
閱讀 2159·2019-08-29 13:19
閱讀 3286·2019-08-28 18:18
閱讀 2625·2019-08-26 13:26
閱讀 584·2019-08-26 11:40
閱讀 1876·2019-08-23 17:17