日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > 架構師社區(qū)
[導讀]Tomcat實現(xiàn)的2個核心功能:處理Socket連接,負責網(wǎng)絡字節(jié)流與Request和Response對象的轉(zhuǎn)化。加載并管理Servlet,以及處理具體的Request請求。所以Tomcat設計了兩個核心組件連接器(Connector)和容器(Container),連接器負責對外...


Tomcat 實現(xiàn)的 2 個核心功能:

  • 處理 Socket 連接,負責網(wǎng)絡字節(jié)流與 RequestResponse 對象的轉(zhuǎn)化。
  • 加載并管理 Servlet ,以及處理具體的 Request 請求。
所以 Tomcat 設計了兩個核心組件連接器(Connector)和容器(Container),連接器負責對外交流,容器負責內(nèi)部處理。

架構篇:Tomcat?高層組件構建一個商業(yè)帝國
Tomcat整體架構
本篇作為 Tomcat 系列的第三篇,帶大家體會 Tomcat 帝國是如何構建的?高層組件如何管理組件的?連接器容器是如何被啟動和管理的?

Tomcat 啟動流程:startup.sh -> catalina.sh start ->java -jar org.apache.catalina.startup.Bootstrap.main()

架構篇:Tomcat?高層組件構建一個商業(yè)帝國
Tomcat 啟動流程
Bootstrap、Catalina、Server、Service、 Engine 都承擔了什么責任?

架構篇:Tomcat?高層組件構建一個商業(yè)帝國
單獨寫一篇介紹他們是因為你可以看到這些啟動類或者組件不處理具體請求,它們的任務主要是管理管理下層組件的生命周期,并且給下層組件分配任務,也就是把請求路由到負責干活兒的組件。

他們就像一個公司的高層,管理整個公司的運作,將任務分配給專業(yè)的人。

我們在設計軟件系統(tǒng)中,不可避免的會遇到需要一些管理作用的組件,就可以學習和借鑒 Tomcat 是如何抽象和管理這些組件的。

因此我把它們比作 Tomcat 的高層,同時愿干活的不再 996。


Bootstrap

當執(zhí)行 startup.sh 腳本的時候,就會啟動一個 JVM 運行 Tomcat 的啟動類 Bootstrapmain 方法。

先看下他的成員變量窺探核心功能:

public?final?class?Bootstrap?{
????ClassLoader?commonLoader?=?null;
????ClassLoader?catalinaLoader?=?null;
????ClassLoader?sharedLoader?=?null;

它的主要任務就是初始化 Tomcat 定義的類加載器,同時創(chuàng)建 Catalina 對象。

Bootstrap 就像一個大神,初始化了類加載器,加載萬物。

關于為何自定義各種類加載器詳情請查看碼哥的 Tomcat 架構設計解析 類加載器部分。

初始化類加載器

架構篇:Tomcat?高層組件構建一個商業(yè)帝國

WebAppClassLoader

假如我們在 Tomcat 中運行了兩個 Web 應用程序,兩個 Web 應用中有同名的 Servlet,但是功能不同,Tomcat 需要同時加載和管理這兩個同名的 Servlet類,保證它們不會沖突,因此 Web 應用之間的類需要隔離。

Tomcat 的解決方案是自定義一個類加載器 WebAppClassLoader, 并且給每個 Web 應用創(chuàng)建一個類加載器實例。

我們知道,Context 容器組件對應一個 Web 應用,因此,每個 Context容器負責創(chuàng)建和維護一個 WebAppClassLoader加載器實例。

這背后的原理是,不同的加載器實例加載的類被認為是不同的類,即使它們的類名相同。

Tomcat 的自定義類加載器 WebAppClassLoader打破了雙親委托機制,它首先自己嘗試去加載某個類,如果找不到則通過 ExtClassLoader 加載 JRE 核心類防止黑客攻擊,無法加載再代理給 AppClassLoader 加載器,其目的是優(yōu)先加載 Web 應用自己定義的類。

具體實現(xiàn)就是重寫 ClassLoader的兩個方法:findClassloadClass。

SharedClassLoader

假如兩個 Web 應用都依賴同一個第三方的 JAR 包,比如 Spring,那 Spring的 JAR 包被加載到內(nèi)存后,Tomcat要保證這兩個 Web 應用能夠共享,也就是說 Spring的 JAR 包只被加載一次。

SharedClassLoader 就是 Web 應用共享的類庫的加載器,專門加載 Web 應用共享的類。

如果 ?WebAppClassLoader自己沒有加載到某個類,就會委托父加載器 SharedClassLoader去加載這個類,SharedClassLoader會在指定目錄下加載共享類,之后返回給 WebAppClassLoader,這樣共享的問題就解決了。

CatalinaClassloader

如何隔離 Tomcat 本身的類和 Web 應用的類?

要共享可以通過父子關系,要隔離那就需要兄弟關系了。

兄弟關系就是指兩個類加載器是平行的,它們可能擁有同一個父加載器,基于此 Tomcat 又設計一個類加載器 CatalinaClassloader,專門來加載 Tomcat 自身的類。

這樣設計有個問題,那 Tomcat 和各 Web 應用之間需要共享一些類時該怎么辦呢?

老辦法,還是再增加一個 CommonClassLoader,作為 CatalinaClassloader和 ?SharedClassLoader 的父加載器。

CommonClassLoader能加載的類都可以被 ?CatalinaClassLoaderSharedClassLoader 使用。

Catalina

Tomcat 是一個公司,Catalina 就好像是一個創(chuàng)始人。因為它負責組建團隊,創(chuàng)建 Server 以及所有子組件。

Catalina 的主要任務就是創(chuàng)建 Server,解析 server.xml 把里面配置的各個組件創(chuàng)建出來,并調(diào)用每個組件的 initstart方法,將整個 Tomcat 啟動,這樣整個公司就在正常運作了。

我們可以根據(jù) Tomcat 配置文件來直觀感受下:

<Server?port="8005"?shutdown="SHUTDOWN">?//?頂層組件,可包含多個?Service,代表一個?Tomcat?實例

??<Service?name="Catalina">??//?頂層組件,包含一個?Engine?,多個連接器
????<Connector?port="8080"?protocol="HTTP/1.1"
???????????????connectionTimeout="20000"
???????????????redirectPort="8443"?/>


????
????<Connector?port="8009"?protocol="AJP/1.3"?redirectPort="8443"?/>??//?連接器

?//?容器組件:一個 Engine 處理 Service 所有請求,包含多個 Host
????<Engine?name="Catalina"?defaultHost="localhost">
???//?容器組件:處理指定Host下的客戶端請求,?可包含多個 Context
??????<Host?name="localhost"??appBase="webapps"
????????????unpackWARs="true"?autoDeploy="true">

???//?容器組件:處理特定 Context Web應用的所有客戶端請求
???<Context>Context>
??????Host>
????Engine>
??Service>
Server>
作為創(chuàng)始人,Catalina 還需要處理公司的各種異常情況,比如有人搶公章(執(zhí)行了 Ctrl C 關閉 Tomcat)。

Tomcat 要如何清理資源呢?

通過向 JVM 注冊一個「關閉鉤子」,具體關鍵邏輯詳見

org.apache.catalina.startup.Catalina#start 源碼:

  1. Server 不存在則解析 server.xml 創(chuàng)建;
  2. 創(chuàng)建失敗則報錯;
  3. 啟動 Server;
  4. 創(chuàng)建并注冊「關閉鉤子」;
  5. await 方法監(jiān)聽停止請求。
???/**
?????*?Start?a?new?server?instance.
?????*/

????public?void?start()?{

????????//?如果?Catalina?持有的?Server?為空則解析?server.xml?創(chuàng)建
????????if?(getServer()?==?null)?{
????????????load();
????????}

????????if?(getServer()?==?null)?{
????????????log.fatal("Cannot?start?server.?Server?instance?is?not?configured.");
????????????return;
????????}

????????//?Start?the?new?server
????????try?{
????????????getServer().start();
????????}?catch?(LifecycleException?e)?{
????????????//?省略部分代碼
????????}

????????//?創(chuàng)建鉤子并注冊
????????if?(useShutdownHook)?{
????????????if?(shutdownHook?==?null)?{
????????????????shutdownHook?=?new?CatalinaShutdownHook();
????????????}
????????????Runtime.getRuntime().addShutdownHook(shutdownHook);
????????}

????????//?監(jiān)聽停止請求,內(nèi)部調(diào)用?Server?的?stop
????????if?(await)?{
????????????await();
????????????stop();
????????}
????}
當我們需要在 JVM 關閉做一些清理工作,比如將緩存數(shù)據(jù)刷到磁盤或者清理一些文件,就可以向 JVM 注冊一個「關閉鉤子」。

它其實就是一個線程,當 JVM 停止前嘗試執(zhí)行這個線程的 run 方法。

org.apache.catalina.startup.Catalina.CatalinaShutdownHook

????protected?class?CatalinaShutdownHook?extends?Thread?{

????????@Override
????????public?void?run()?{
????????????try?{
????????????????if?(getServer()?!=?null)?{
????????????????????Catalina.this.stop();
????????????????}
????????????}?catch?(Throwable?ex)?{
??????????????//?省略部分代碼....
????????????}
????????}
????}
其實就是執(zhí)行了 Catalina 的 stop 方法,通過它將整個 Tomcat 停止。

Server

Server 組件的職責就是管理 Service 組件,負責調(diào)用持有的 Servicestart 方法。

他就像是公司的 CEO,負責管理多個事業(yè)部,每個事業(yè)部就是一個 Service。

它管理兩個部門:

  • Connector 連接器:對外市場營銷部,推廣吹牛寫 PPT 的。
  • Container 容器:研發(fā)部門,沒有性生活的 996 。
實現(xiàn)類是 org.apache.catalina.core.StandardServer,Server 繼承 org.apache.catalina.util.LifecycleMBeanBase,所以他的生命周期也被統(tǒng)一管理,Server 的子組件是 Service,所以還需要管理 Service 的生命周期。

也就是說在啟動和關閉 Server 的時候會分別先調(diào)用 Service 的 啟動和停止方法。

這就是設計思想呀,抽象出生命周期 Lifecycle 接口,體現(xiàn)出接口隔離原則,將生命周期的相關功能內(nèi)聚。

架構篇:Tomcat?高層組件構建一個商業(yè)帝國
我們接著看 Server 如何管理 Service 的,核心源碼如下org.apache.catalina.core.StandardServer#addService:

public?void?addService(Service?service)?{

????????service.setServer(this);

????????synchronized?(servicesLock)?{
????????????//?創(chuàng)建?長度? 1?的數(shù)組
????????????Service?results[]?=?new?Service[services.length? ?1];
????????????//?將舊的數(shù)據(jù)復制到新數(shù)組
????????????System.arraycopy(services,?0,?results,?0,?services.length);
????????????results[services.length]?=?service;
????????????services?=?results;
????????????//?啟動?Service?組件
????????????if?(getState().isAvailable())?{
????????????????try?{
????????????????????service.start();
????????????????}?catch?(LifecycleException?e)?{
????????????????????//?Ignore
????????????????}
????????????}

????????????//?發(fā)送事件
????????????support.firePropertyChange("service",?null,?service);
????????}

????}
在添加 Service 過程中動態(tài)拓展數(shù)組長度,為了節(jié)省內(nèi)存。

除此之外,Server 組件還有一個重要的任務是啟動一個 Socket 來監(jiān)聽停止端口,這就是為什么你能通過 shutdown 命令來關閉 Tomcat。

不知道你留意到?jīng)]有,上面 Caralina 的啟動方法的最后一行代碼就是調(diào)用了 Server 的 await 方法。

在 await 方法里會創(chuàng)建一個 Socket 監(jiān)聽 8005 端口,并在一個死循環(huán)里接收 Socket 上的連接請求,如果有新的連接到來就建立連接,然后從 Socket 中讀取數(shù)據(jù);如果讀到的數(shù)據(jù)是停止命令“SHUTDOWN”,就退出循環(huán),進入 stop 流程。

Service

他的職責就是管理 Connector 連接器頂層容器 Engine,會分別調(diào)用他們的 start 方法。至此,整個 Tomcat 就算啟動完成了。

Service 就是事業(yè)部的話事人,管理兩個職能部門對外推廣部(連接器),對內(nèi)研發(fā)部(容器)。

Service 組件的實現(xiàn)類是org.apache.catalina.core.StandardService,直接看關鍵的成員變量。

public?class?StandardService?extends?LifecycleMBeanBase?implements?Service?{
????//?名字
????private?String?name?=?null;
????
????//?所屬的?Server?實例
????private?Server?server?=?null;
?
????//?連接器數(shù)組
????protected?Connector?connectors[]?=?new?Connector[0];
????private?final?Object?connectorsLock?=?new?Object();
?
????//?對應的?Engine?容器
????private?Engine?engine?=?null;
????
????//?映射器及其監(jiān)聽器
????protected?final?Mapper?mapper?=?new?Mapper();
????protected?final?MapperListener?mapperListener?=?new?MapperListener(this);

繼承 LifecycleMBeanBase 而 ?LifecycleMBeanBase 又繼承 LifecycleBase,這里實際上是模板方法模式的運用,org.apache.catalina.util.LifecycleBase#initorg.apache.catalina.util.LifecycleBase#start,org.apache.catalina.util.LifecycleBase#stop 分別是對應的模板方法,內(nèi)部定義了整個算法流程,子類去實現(xiàn)自己內(nèi)部具體變化部分,將變與不變抽象出來實現(xiàn)開閉原則設計思路。

那為什么還有一個 MapperListener?這是因為 Tomcat 支持熱部署,當 Web 應用的部署發(fā)生變化時,Mapper 中的映射信息也要跟著變化,MapperListener 就是一個監(jiān)聽器,它監(jiān)聽容器的變化,并把信息更新到 Mapper 中,這是典型的觀察者模式。

作為“管理”角色的組件,最重要的是維護其他組件的生命周期。

此外在啟動各種組件時,要注意它們的依賴關系,也就是說,要注意啟動的順序。我們來看看 Service 啟動方法:

protected?void?startInternal()?throws?LifecycleException?{
?
????//1.?觸發(fā)啟動監(jiān)聽器
????setState(LifecycleState.STARTING);
?
????//2.?先啟動?Engine,Engine?會啟動它子容器
????if?(engine?!=?null)?{
????????synchronized?(engine)?{
????????????engine.start();
????????}
????}
????
????//3.?再啟動?Mapper?監(jiān)聽器
????mapperListener.start();
?
????//4.?最后啟動連接器,連接器會啟動它子組件,比如?Endpoint
????synchronized?(connectorsLock)?{
????????for?(Connector?connector:?connectors)?{
????????????if?(connector.getState()?!=?LifecycleState.FAILED)?{
????????????????connector.start();
????????????}
????????}
????}
}
這里啟動順序也很講究,Service 先啟動了 Engine 組件,再啟動 Mapper 監(jiān)聽器,最后才是啟動連接器。

這很好理解,因為內(nèi)層組件啟動好了才能對外提供服務,產(chǎn)品沒做出來,市場部也不能瞎忽悠,研發(fā)好了才能啟動外層的連接器組件。

而 Mapper 也依賴容器組件,容器組件啟動好了才能監(jiān)聽它們的變化,因此 Mapper 和 MapperListener 在容器組件之后啟動。

組件停止的順序跟啟動順序正好相反的,也是基于它們的依賴關系。

Engine

他就是一個研發(fā)部的頭頭,是最頂層的容器組件。繼承 Container,所有的容器組件都繼承 Container,這里實際上運用了組合模式統(tǒng)一管理。

他的實現(xiàn)類是 org.apache.catalina.core.StandardEngine,繼承 ContainerBase

public?class?StandardEngine?extends?ContainerBase?implements?Engine?{
}
他的子容器是 Host,所以持有 Host 容器數(shù)組,這個屬性每個容器都會存在,所以放在抽象類中

protected?final?HashMap?children?=?new?HashMap<>();
ContainerBase 用 HashMap 保存了它的子容器,并且 ContainerBase 還實現(xiàn)了子容器的“增刪改查”,甚至連子組件的啟動和停止都提供了默認實現(xiàn),比如 ContainerBase 會用專門的線程池來啟動子容器。

org.apache.catalina.core.ContainerBase#startInternal

//?Start?our?child?containers,?if?any
Container?children[]?=?findChildren();
List>?results?=?new?ArrayList<>();
for?(Container?child?:?children)?{
??results.add(startStopExecutor.submit(new?StartChild(child)));
}
Engine 在啟動 Host 子容器時就直接重用了這個方法。

容器組件最重要的功能是處理請求,而 Engine 容器對請求的“處理”,其實就是把請求轉(zhuǎn)發(fā)給某一個 Host 子容器來處理,具體是通過 Valve 來實現(xiàn)的。

每一個容器組件都有一個 Pipeline,而 Pipeline 中有一個基礎閥(Basic Valve),透過構造方法創(chuàng)建 Pipeline。

public?StandardEngine()?{

????super();
????pipeline.setBasic(new?StandardEngineValve());
????//?省略部分代碼

}
Engine 容器的基礎閥定義如下:

final?class?StandardEngineValve?extends?ValveBase?{
?
????public?final?void?invoke(Request?request,?Response?response)
??????throws?IOException,?ServletException?
{
??
??????//?拿到請求中的?Host?容器
??????Host?host?=?request.getHost();
??????if?(host?==?null)?{
??????????return;
??????}
??
??????//?調(diào)用?Host?容器中的?Pipeline?中的第一個?Valve
??????host.getPipeline().getFirst().invoke(request,?response);
??}
??
}
這個基礎閥實現(xiàn)非常簡單,就是把請求轉(zhuǎn)發(fā)到 Host 容器。

從代碼中可以看到,處理請求的 Host 容器對象是從請求中拿到的,請求對象中怎么會有 Host 容器呢?

這是因為請求到達 Engine 容器中之前,Mapper 組件已經(jīng)對請求進行了路由處理,Mapper 組件通過請求的 URL 定位了相應的容器,并且把容器對象保存到了請求對象中。

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅(qū)動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅(qū)動性能的關鍵。

關鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質(zhì)量和效率直接關系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅(qū)動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關鍵字: LED 驅(qū)動電源 開關電源

LED驅(qū)動電源是把電源供應轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅(qū)動電源
關閉