日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

聊聊Tomcat的架构设计

發布時間:2025/3/18 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊Tomcat的架构设计 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

微信公眾號「后端進階」,專注后端技術分享:Java、Golang、WEB框架、分布式中間件、服務治理等等。
老司機傾囊相授,帶你一路進階,來不及解釋了快上車!

Tomcat 是 Java WEB 開發接觸最多的 Servlet 容器,但它不僅僅是一個 Servlet 容器,它還是一個 WEB 應用服務器,在微服務架構體系下,為了降低部署成本,減少資源的開銷,追求的是輕量化與穩定,而 Tomcat 是一個輕量級應用服務器,自然被很多開發人員所接受。

Tomcat 里面藏著很多值得我們每個 Java WEB 開發者學習的知識,可以這么說,當你弄懂了 Tomcat 的設計原理,Java WEB 開發對你來說已經沒有什么秘密可言了。本篇文章主要是跟大家聊聊 Tomcat 的內部架構體系,讓大家對 Tomcat 有個整體的認知。

前面我也說了,Tomcat 的本質其實就是一個 WEB 服務器 + 一個 Servlet 容器,那么它必然需要處理網絡的連接與 Servlet 的管理,因此,Tomcat 設計了兩個核心組件來實現這兩個功能,分別是連接器和容器,連接器用來處理外部網絡連接,容器用來處理內部 Servlet,我用一張圖來表示它們的關系:

一個 Tomcat 代表一個 Server 服務器,一個 Server 服務器可以包含多個 Service 服務,Tomcat 默認的 Service 服務是 Catalina,而一個 Service 服務可以包含多個連接器,因為 Tomcat 支持多種網絡協議,包括 HTTP/1.1、HTTP/2、AJP 等等,一個 Service 服務還會包括一個容器,容器外部會有一層 Engine 引擎所包裹,負責與處理連接器的請求與響應,連接器與容器之間通過 ServletRequest 和 ServletResponse 對象進行交流。

也可以從 server.xml 的配置結構可以看出 tomcat 整體的內部結構:

<Server port="8005" shutdown="SHUTDOWN"><Service name="Catalina"><Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/><Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/><Engine defaultHost="localhost" name="Catalina"><Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true"><Context docBase="handler-api" path="/handler" reloadable="true" source="org.eclipse.jst.jee.server:handler-api"/></Host></Engine></Service> </Server>

連接器(Connector)

連接器負責將各種網絡協議封裝起來,對外部屏蔽了網絡連接與 IO 處理的細節,將處理得到的 Request 對象傳遞給容器處理,Tomcat 將處理請求的細節封裝到 ProtocolHandler,ProtocolHandler 是一個接口類型,通過實現 ProtocolHandler 來實現各種協議的處理,如 Http11AprProtocol:

ProtocolHandler 采用組件模式的設計,將處理網絡連接,字節流封裝成 Request 對象,再將 Request 適配成 Servlet 處理 ServletRequest 對象這幾個動作,用組件封裝起來了,ProtocolHandler 包括了三個組件:Endpoint、Processor、Adapter。

Endpoint 在 ProtocolHandler 實現類的構造方法中創建,如下:

public Http11AprProtocol() {super(new AprEndpoint()); }

Endpoint 組件用來處理底層的 Socket 網絡連接,AprEndpoint 里面有個叫 SocketProcessor 的內部類,它負責為 AprEndpoint 將接收到的 Socket 請求轉化成 Request 對象,SocketProcessor 實現了 Runnable 接口,它會有一個專門的線程池來處理,后面我會單獨從源碼的角度分析 Endpoint 組件的設計原理。

org.apache.tomcat.util.net.AprEndpoint.SocketProcessor#doRun:

// Process the request from this socket SocketState state = getHandler().process(socketWrapper, event);

process 方法會創建一個 processor 對象,調用它的 process 方法將 Socket 字節流封裝成 Request 對象,在創建 Processor 組件時,會將 Adapter 組件添加到 Processor 組件中:

org.apache.coyote.http11.AbstractHttp11Protocol#createProcessor:

protected Processor createProcessor() {Http11Processor processor = new Http11Processor();// set Adapter 組件processor.setAdapter(getAdapter());return processor; }

而 Adapter 組件在連接器初始化時就已經創建好了:

org.apache.catalina.connector.Connector#initInternal:

// Initialize adapter adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter);

目前為止,Tomcat 只有一個 Adapter 實現類,就是 CoyoteAdapter。Adapter 的主要作用是將 Request 對象適配成容器能夠識別的 Request 對象,比如 Servlet 容器,它的只能識別 ServletRequest 對象,這時候就需要 Adapter 適配器類作一層適配。

以上連接器的各個組件,我用一張圖說明它們直接的關系:

容器(Container)

在 Tomcat 中一共設計了 4 種容器,它們分別為 Engine、Host、Context、Wrapper,它們的關系如下圖所示:

  • Engine:表示一個虛擬主機的引擎,一個 Tomcat Server 只有一個 引擎,連接器所有的請求都交給引擎處理,而引擎則會交給相應的虛擬主機去處理請求;
  • Host:表示虛擬主機,一個容器可以有多個虛擬主機,每個主機都有對應的域名,在 Tomcat 中,一個 webapps 就代表一個虛擬主機,當然 webapps 可以配置多個;
  • Context:表示一個應用容器,一個虛擬主機可以擁有多個應用,webapps 中每個目錄都代表一個 Context,每個應用可以配置多個 Servlet。

從上圖可看出,各個容器組件之間的關系是由大到小,即父子關系,它們之間關系形成一個樹狀的結構,它們的實現類都實現了 Container 接口,它有如下方法來控制容器組件之間的關系:

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); }

容器組件之間通過以上幾個方法,即可實現它們之間的父子關系,有沒有發現,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 中有很多組件,組件通過實現 Lifecycle 接口,Tomcat 通過事件機制來實現對這些組件生命周期的管理。

Tomcat 的這種容器設計思想,其實是運用了組合設計模式的思想,組合設計模式最大的優點是可以自由添加節點,這樣也就使得 Tomcat 的容器組件非常地容易進行擴展,符合設計模式中的開閉原則。

現在我們知道了 Tomcat 的容器組件的組合方式,那我們現在就來想一個問題:

當一個請求過來時,Tomcat 是如何識別請求并將它交給特定 Servlet 來處理呢?

從容器的組合關系可以看出,它們調用順序必定是:

Engine -> Host -> Context -> Wrapper -> Servlet

那么 Tomcat 是如何來定位 Servlet 的呢?答案是利用 Mapper 組件來完成定位的工作。

Mapper 最主要的核心功能是保存容器組件之間訪問路徑的映射關系,它是如何做到這點的呢?

我們不妨先從源碼入手:

org.apache.catalina.core.StandardService:

protected final Mapper mapper = new Mapper(); protected final MapperListener mapperListener = new MapperListener(this);

Service 實現類中,已經初始化了 Mapper 組件以及它的監聽類 MapperListener,這里先說明一下,在 Tomcat 組件中,標準的實現組件類前綴會有 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 服務啟動的時候,會調用 MapperListener.start() 方法,最終會執行 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 wrappersregisterHost(host);} }

該方法會注冊新的虛擬主機,接著 registerHost() 方法會注冊 context,以此類推,從而將容器組件直接的訪問的路徑都注冊到 Mapper 中。

定位 Servlet 的流程圖:

轉載于:https://my.oschina.net/objcoding/blog/3055999

總結

以上是生活随笔為你收集整理的聊聊Tomcat的架构设计的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。