淺談Tomcat多層容器的設計
目錄
- 容器的層次結構
- 請求定位Servlet的過程
- 工作原理
Tomcat的容器用來裝載Servlet。那Tomcat的Servlet容器是如何設計的呢?
容器的層次結構
Tomcat設計了4種容器:Engine、Host、Context和Wrapper
Tomcat通過這種分層,使得Servlet容器具有很好的靈活性。
- Context表示一個Web應用程序
- Wrapper表示一個Servlet,一個Web應用程序中可能會有多個Servlet
- Host代表一個虛擬主機,或一個站點,可以給Tomcat配置多個虛擬主機地址,而一個虛擬主機下可以部署多個Web應用程序
- Engine表示引擎,用來管理多個虛擬站點,一個Service最多只能有一個Engine
觀察Tomcat的server.xml配置文件。Tomcat采用了組件化設計,最外層即是Server
這些容器具有父子關系,形成一個樹形結構,Tomcat用組合模式來管理這些容器。
所有容器組件都實現Container接口,因此組合模式可以使得用戶對
單容器對象
最底層的Wrapper
組合容器對象
上面的Context、Host或者Engine
的使用具有一致性。
Container接口定義:
public interface Container extends Lifecycle { public void setName(String name); public Container getParent(); public void setParent(Container container); public void addChild(Container child); public void removeChild(Container child); public Container findChild(String name);}
請求定位Servlet的過程
搞這么多層次的容器,Tomcat是怎么確定請求是由哪個Wrapper容器里的Servlet來處理的呢?
Tomcat用Mapper組件完成這個任務。
Mapper就是將用戶請求的URL定位到一個Servlet
工作原理
Mapper組件保存了Web應用的配置信息:容器組件與訪問路徑的映射關系,比如
- Host容器里配置的域名
- Context容器里的Web應用路徑
- Wrapper容器里Servlet映射的路徑
這些配置信息就是一個多層次的Map。
當一個請求到來時,Mapper組件通過解析請求URL里的域名和路徑,再到自己保存的Map里去查找,就能定位到一個Servlet。
一個請求URL最后只會定位到一個Wrapper容器,即一個Servlet。
假如有一網購系統,有
- 面向B端管理人員的后臺管理系統
- 面向C端用戶的在線購物系統
這倆系統跑在同一Tomcat,為隔離它們的訪問域名,配置兩個虛擬域名:
manage.shopping.com
管理人員通過該域名訪問Tomcat去管理用戶和商品,而用戶管理和商品管理是兩個單獨的Web應用
user.shopping.com
C端用戶通過該域名去搜索商品和下訂單,搜索功能和訂單管理也是兩個獨立Web應用
這樣部署,Tomcat會創建一個Service組件和一個Engine容器組件,在Engine容器下創建兩個Host子容器,在每個Host容器下創建兩個Context子容器。由于一個Web應用通常有多個Servlet,Tomcat還會在每個Context容器里創建多個Wrapper子容器。每個容器都有對應訪問路徑
Tomcat如何將URL定位到一個Servlet呢?
首先,根據協議和端口號選定Service和Engine
Tomcat的每個連接器都監聽不同的端口,比如Tomcat默認的HTTP連接器監聽8080端口、默認的AJP連接器監聽8009端口。該URL訪問8080端口,因此會被HTTP連接器接收,而一個連接器是屬于一個Service組件的,這樣Service組件就確定了。一個Service組件里除了有多個連接器,還有一個Engine容器,因此Service確定了,Engine也確定了。
根據域名選定Host。
Mapper組件通過URL中的域名去查找相應的Host容器,比如user.shopping.com,因此Mapper找到Host2容器。
根據URL路徑找到Context組件
Host確定以后,Mapper根據URL的路徑來匹配相應的Web應用的路徑,比如例子中訪問的是/order,因此找到了Context4這個Context容器。
最后,根據URL路徑找到Wrapper(Servlet)
Context確定后,Mapper再根據web.xml中配置的Servlet映射路徑來找到具體Wrapper和Servlet。
并非只有Servlet才會去處理請求,查找路徑上的父子容器都會對請求做一些處理:
- 連接器中的Adapter會調用容器的Service方法執行Servlet
- 最先拿到請求的是Engine容器,Engine容器對請求做一些處理后,會把請求傳給自己子容器Host繼續處理,依次類推
- 最后這個請求會傳給Wrapper容器,Wrapper會調用最終的Servlet來處理
這個調用過程使用的Pipeline-Valve管道,責任鏈模式,在一個請求處理的過程中有很多處理者依次對請求進行處理,每個處理者負責做自己相應的處理,處理完之后將再調用下一個處理者繼續處理。
Valve表示一個處理點,比如權限認證和記錄日志。
public interface Valve { public Valve getNext(); public void setNext(Valve valve); public void invoke(Request request, Response response)}
由于Valve是一個處理點,因此invoke方法就是來處理請求的。
Pipeline接口:
public interface Pipeline extends Contained { public void addValve(Valve valve); public Valve getBasic(); public void setBasic(Valve valve); public Valve getFirst();}
所以Pipeline中維護了Valve鏈表,Valve可插入到Pipeline。
Pipeline中沒有invoke方法,因為整個調用鏈的觸發是Valve完成自己的處理后,調用getNext.invoke調用下一個Valve。
每個容器都有一個Pipeline對象,只要觸發這個Pipeline的第一個Valve,這個容器里Pipeline中的Valve就都會被調用到。但不同容器的Pipeline如何鏈式觸發?
比如Engine中Pipeline需要調用下層容器Host中的Pipeline。
Pipeline有個getBasic方法。這個BasicValve處于Valve鏈尾,負責調用下層容器的Pipeline里的第一個Valve。
整個調用過程由連接器中的Adapter觸發的,它會調用Engine的第一個Valve:
Wrapper
容器的最后一個Valve會創建一個Filter鏈,并調用doFilter方法,最終會調到Servlet的service方法。
Valve和Filter有什么區別呢?
- Valve是Tomcat的私有機制,與Tomcat緊耦合。Servlet API是公有標準,所有Web容器包括Jetty都支持Filter
- Valve工作在Web容器級別,攔截所有應用的請求。Servlet Filter工作在應用級別,只攔截某個Web應用的所有請求。若想做整個Web容器的攔截器,必須使用Valve。
到此這篇關于淺談Tomcat多層容器的設計的文章就介紹到這了,更多相關Tomcat 多層容器內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!
相關文章:
1. 淺談Tomcat內存配置的正確姿勢2. Tomcat生命周期詳解3. Tomcat配置必備的10個小技巧用法總結4. 關于Tomcat啟動失敗報循環依賴的問題解決(AncestorAxisIterator)5. 關于Keytool配置 Tomcat的HTTPS雙向認證的問題6. 使用JMX監控Tomcat示例代碼7. Tomcat中修改server.xml和content.xml后自動還原問題解決8. 傳統tomcat啟動服務與springboot啟動內置tomcat服務的區別(推薦)9. 使用Tomcat無法訪問http:localhost:8080的解決方法10. idea配置tomcat必坑指南圖文詳解
