tomcat學習步驟,附帶打破雙親委派模型企業應用實戰


1. tomcat入門

入門模塊僅做學習大綱梳理,忽略了具體操作指引。

Tomcat的三種部署模式:

簡單架構模型

 

連接器的非阻塞模式(NIO)

                                                                                                                                              通道(Channel)、緩沖區(Buffer)、選擇器(Selector)

 

容器container的責任鏈模式:

1.請求被Connector組件接收,創建Request和Response對象。

2.Connector將Request和Response交給Container,先通過Engine的pipeline組件流經內部的每個Valve。

3.請求流轉到Host的pipeline組件中,並且經過內部Valve的過。

4.請求流轉到Context的pipeline組件中,並且經過內部的Valve的過濾。

5.請求流轉到Wrapper的pipeline組件中,並且經過內部的Valve的過濾。

6.Wrapper內部的WrapperValve創建FilterChain實例,調用指定的Servlet實例處理請求。

7.返回。

 

 

2.tomcat架構進階

 

 

tomcat頂層結構

 

 

 

 

Server:服務器的意思,代表整個tomcat服務器,一個tomcat只有一個Server;

Service:Server中的一個邏輯功能層, 一個Server可以包含多個Service;

Connector:稱作連接器,是Service的核心組件之一,一個Service可以有多個Connector,主要是連接客戶端請求;

Container:Service的另一個核心組件,按照層級有Engine,Host,Context,Wrapper四種,一個Service只有一個Engine,其主要作用是執

行業務邏輯;
Jasper:JSP引擎;
Session:會話管理;

 

Connector解析

 

 

 

Connector使用ProtocolHandler來處理請求的Endpoint用來處理底層Socket的網絡連接。

Processor用於將Endpoint接收到的Socket封裝成Request。

Adapter充當適配器,用於將Request轉換為ServletRequest交給Container進行具體的處理。

ProtocolHandler由包含了三個部件:Endpoint、Processor,Adapter

Container解析

 

 

service中的name是catalina,engine中的name也是catalina,這個就證明一個service就只有一個engine, 一個engine可以有多個host主機

host,站點,虛擬主機。

Engine:引擎、只有一個定義了一個名為Catalina的Engine

Host:站點、虛擬主機一個Engine包含多個Host的設計,使得一個服務器實例可以承擔多個域名的服務,是很靈活的設計

Context:一個應用默認配置下webapps下的每個目錄都是一個應用

Wrapper:一個Servlet

 

結合tomcat目錄結構理解上面的4個容器之間的關系

3.tomcat類加載機制

引用自:https://www.cnblogs.com/ghoster/p/7602158.html

 

1.主流的Java Web服務器,如Tomcat、Jetty、WebLogic、WebSphere等都實現了自己定義的類加載器(一般都不止一個)。因為一個功能健全的web服務器要解決一下幾個問題:

    1)部署在一個服務器上的兩個web應用程序所使用的Java類庫可以實現互相隔離。這是最基本的需求,兩個不同的應用程序可能會依賴同一個第三方類庫的不同版本,不能要求一個類庫在一個服務器中只有一份,服務器應當保證兩個應用程序的類庫可以互相獨立使用

    2)部署在同一個服務器上的兩個Web應用程序所使用的Java類庫可以互相共享。這個需求也非常很常見,如,用戶可能有10各使用Spring組織的應用程序部署在同一台服務器上,如果把10份Spring分別存放在各個應用程序的隔離目錄中,將會是很大的資源浪費-這主要不是浪費磁盤空間的問題,而是指類庫在使用時都要被加載到服務器內存,如果類庫不能共享,虛擬機的方法區就會很容易出現過度膨脹的風險

    3)服務器需要盡可能地保證自身的安全不受部署的Web應用程序影響。目前,有許多主流的Java Web服務器自身也是使用Java語言來實現的。因此,服務器本身也有類庫依賴的問題,一般來說,基於安全考慮,服務器所使用的類庫應該與應用程序的類庫相互獨立

    4)支持jsp應用的Web服務器,大多數都需要支持HotSwap功能。我們知道,jsp文件最終要編譯成Java Class才能由虛擬機執行,但jsp文件由於其純文本存儲的特性,運行時修改的概率遠遠大於第三方類庫或程序自身的Class文件。而且ASP、PHP和JSP這些網頁應用也把修改后無需重啟作為一個很大的“優勢”來看待,因此,“主流”的Web服務器都會支持JSP的熱替換,當然也有“非主流”的,如運行在生產模式下的WebLogic服務器默認就不會處理JSP文件的變化

  由於存在上述問題,在部署Web應用時,單獨的一個ClassPath就無法滿足需求了,所以各種Web服務器都“不約而同”地提供了好幾個ClassPath路徑供用戶存放第三方類庫,這些路徑一般都以lib或classes命名,被放置到不同路徑中的類庫,具備不同的訪問范圍和服務對象,通常,每一個目錄都會有一個相應的自定義類加載器去加載放置在里面的Java類庫。

  2.Tomcat對用戶類庫與類加載器的規划

    在其目錄結構下有三組目錄(“/common/*”、“/server/*”、“/shared/*”)可以存放Java類庫,另外還可以加上Web應用程序本身的目錄“/WEB-INF/*”,一共4組,把Java類庫放置在這些目錄中的含義分別如下:

    1)放置在/commom目錄中:類庫可被Tomcat和所有的Web應用程序共同使用

    2)放置在/server目錄中:類庫可被Tomcat使用,對所有的Web應用程序都不可見

    3)放置在/shared目錄中:類庫可被所有的Web應用程序所共同使用,但對Tomcat自己不可見

    4)放置在/WebApp/WEB-INF目錄中:類庫僅僅可以被此Web應用程序使用,對Tomcat和其他Web應用程序都不可見

    為了支持這套目錄結構,並對目錄里面的類庫進行加載和隔離,Tomcat自定義了多個類加載器,這些類加載器按照經典的雙親委派模型來實現,所下圖:

    

    最上面的三個類加載器是JDK默認提供的類加載器,這三個加載器的的作用之前也說過,這里不再贅述了,可以在http://www.cnblogs.com/ghoster/p/7594224.html簡單了解。而CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebAppClassLoader則是Tomcat自己定義的類加載器,他們分別加載/common/*、/server/*、/shared/*和/WebApp/WEB-INF/*中的Java類庫。其中WebApp類加載器和jsp類加載器通常會存在多個實例,每一個Web應用程序對應一個WebApp類加載器,每一個jsp文件對應一個Jsp類加載器

    從上圖的委派關系可以看出,CommonClassLoader能加載的類都可以被CatalinaClassLoader和SharedClassLoader使用,而CatalinaClassLoader和SharedClassLoader自己能加載的類則與對方相互隔離。WebAppClassLoader可以使用SharedClassLoader加載到的類,但各個WebAppClassLoader實例之間相互隔離。而JasperLoader的加載范圍僅僅是這個JSP文件所編譯出來的哪一個Class,它出現的目的就是為了被丟棄:當服務器檢測到JSP文件被修改時,會替換掉目前的JasperLoader的實例,並通過在建立一個新的Jsp類加載器來實現JSP文件的HotSwap功能

 

4.打破雙親委派模型企業級應用實戰

需求背景:在使用多數據源的情況下,同時連接某大廠魔改的數據庫mppdb和pgsql數據庫的時候。出現了無法同時加載mppdb和pgsql的驅動的問題。

 

原因分析:spring在啟動時會調用應用類加載器加載org目錄下的.class文件,同時spring自定義類加載器分別加載BOOT-INF,META-INF 目錄下的文件,mppdb 與pgsql驅動的本質都是.jar文件,所以會放在META-INF目錄下的lib目錄下。

又因為mppdb是某國內大廠以pgsql為藍本魔改的,遺留下一個問題,mppdb與mysql驅動的類名是完全一致的。而兩驅動都放在lib目錄下,由同一個類加載器來加載,就會出現以下情況:當第一個驅動pgsql加載時,使用雙親機制正常加載,

當第二個驅動mppdb再次加載時,會判斷該類是否加載過,此時,由於類加載器一致,類名一致,類加載器會誤認為mppdb驅動已經加載過了,實際是之前加載的pgsql驅動,所以spring不會再去加載mppdb,而是從緩沖中把pgsql的驅動拋出,

從而導致了,spring在啟動時無法同時加載mppdn驅動和pgsql驅動。

 

解決方案:

方案一:遵循雙親委派模型

在spring容器啟動后(此時spring已經加載到其中一個驅動,假設是pgsql驅動),新開一個jvm進程,在該進程中使用擴展類加載器加載mppdb驅動,根據雙親委派模型,擴展類加載器是應用類加載器的父類,當需要加載驅動時會優先委派父類加載,

所以同一個類在父類加載器和子類加載器都能加載的情況下,父類加載器總能優先加載的。這樣就保證了,新起的進程總是能加載到擴展類加載器加載的mppdb驅動。

方案二:打破雙親委派模型

在spring容器啟動后(此時spring已經加載到其中一個驅動,假設是pgsql驅動),不必新開進程,還是在該spring的進程內,使用應用類加載器XMLClassLoader去加載mppdb驅動,如果此時使用雙親委派模型,會加載到spring初始化時加載到的驅動,即pgsql驅動,

故而 需要將XMLClassLoader 的父加載器設置為null,禁止其父加載器加載,而由其自己加載,即可以成功加載mppdb驅動,具體實現涉及公司信息安全,在此僅提供問題解決思路。

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM