6張圖說清楚Tomcat原理及請求流程

前言

不少東西在時序圖中體現的已經很是清楚了,沒有必要再一步一步的做介紹,本文以圖爲主,而後對部份內容加以簡單解釋。
  • 繪製圖形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension
本文對 Tomcat 的介紹以 Tomcat-9.0.0.M22 爲標準。
Tomcat-9.0.0.M22 是 Tomcat 目前最新的版本,但還沒有發佈,它實現了 Servlet4.0 及 JSP2.3 並提供了不少新特性,須要 1.8 及以上的 JDK 支持等等,詳情請查閱 Tomcat-9.0-doc
  • Overview
  • Connector Init and Start
  • Requtst Process
  • Acceptor
  • Poller
  • Worker
  • Container
  • At last

Overview

  1. Connector 啓動之後會啓動一組線程用於不一樣階段的請求處理過程。
  2. Acceptor 線程組。用於接受新鏈接,並將新鏈接封裝一下,選擇一個 Poller 將新鏈接添加到 Poller 的事件隊列中。
  3. Poller 線程組。用於監聽 Socket 事件,當 Socket 可讀或可寫等等時,將 Socket 封裝一下添加到 worker 線程池的任務隊列中。
  4. worker 線程組。用於對請求進行處理,包括分析請求報文並建立 Request 對象,調用容器的 pipeline 進行處理。
  • Acceptor、 Poller、 worker 所在的 ThreadPoolExecutor 都維護在 NioEndpoint 中。

Connector Init and Start

  1. initServerSocket(),經過 ServerSocketChannel.open() 打開一個 ServerSocket,默認綁定到 8080 端口,默認的鏈接等待隊列長度是 100, 當超過 100 個時會拒絕服務。咱們能夠經過配置 conf/server.xml 中 Connector 的 acceptCount 屬性對其進行定製。
  2. createExecutor() 用於建立 Worker 線程池。默認會啓動 10 個 Worker 線程,Tomcat 處理請求過程當中,Woker 最多不超過 200 個。咱們能夠經過配置 conf/server.xml 中 Connector 的 minSpareThreads 和 maxThreads 對這兩個屬性進行定製。
  3. Pollor 用於檢測已就緒的 Socket。 默認最多不超過 2 個, Math.min(2,Runtime.getRuntime().availableProcessors());。咱們能夠經過配置 pollerThreadCount 來定製。
  4. Acceptor 用於接受新鏈接。默認是 1 個。咱們能夠經過配置 acceptorThreadCount 對其進行定製。

Requtst Process

Acceptor

  1. Acceptor 在啓動後會阻塞在 ServerSocketChannel.accept(); 方法處,當有新鏈接到達時,該方法返回一個 SocketChannel。
  2. 配置完 Socket 之後將 Socket 封裝到 NioChannel 中,並註冊到 Poller, 值的一提的是,咱們一開始就啓動了多個 Poller 線程,註冊的時候,鏈接是公平的分配到每一個 Poller 的。 NioEndpoint 維護了一個 Poller 數組,當一個鏈接分配給 pollers[index] 時,下一個鏈接就會分配給 pollers[(index+1)%pollers.length].
  3. addEvent() 方法會將 Socket 添加到該 Poller 的 PollerEvent 隊列中。到此 Acceptor 的任務就完成了。

Poller

  1. selector.select(1000)。當 Poller 啓動後由於 selector 中並無已註冊的 Channel,因此當執行到該方法時只能阻塞。全部的 Poller 共用一個 Selector,其實現類是 sun.nio.ch.EPollSelectorImpl
  2. events() 方法會將經過 addEvent() 方法添加到事件隊列中的 Socket 註冊到 EPollSelectorImpl,當 Socket 可讀時, Poller 纔對其進行處理
  3. createSocketProcessor() 方法將 Socket 封裝到 SocketProcessor 中, SocketProcessor 實現了 Runnable 接口。 worker 線程經過調用其 run() 方法來對 Socket 進行處理。
  4. execute(SocketProcessor) 方法將 SocketProcessor 提交到線程池,放入線程池的 workQueue 中。 workQueue 是 BlockingQueue 的實例。到此 Poller 的任務就完成了。

Worker

  1. worker 線程被建立之後就執行 ThreadPoolExecutor 的 runWorker() 方法,試圖從 workQueue 中取待處理任務,可是一開始 workQueue 是空的,因此 worker 線程會阻塞在 workQueue.take() 方法。
  2. 當新任務添加到 workQueue後, workQueue.take() 方法會返回一個 Runnable,一般是 SocketProcessor, 而後 worker 線程調用 SocketProcessor 的 run() 方法對 Socket 進行處理。
  3. createProcessor() 會建立一個 Http11Processor, 它用來解析 Socket,將 Socket 中的內容封裝到 Request 中。注意這個 Request 是臨時使用的一個類,它的全類名是 org.apache.coyote.Request,
  4. postParseRequest() 方法封裝一下 Request,並處理一下映射關係 (從 URL 映射到相應的 Host、 Context、 Wrapper)。
  5. CoyoteAdapter 將 Rquest 提交給 Container 處理以前,並將 org.apache.coyote.Request 封裝到 org.apache.catalina.connector.Request,傳遞給 Container 處理的 Request 是 org.apache.catalina.connector.Request。
  6. connector.getService().getMapper().map(),用來在 Mapper 中查詢 URL 的映射關係。映射關係會保留到 org.apache.catalina.connector.Request 中, Container 處理階段 request.getHost() 是使用的就是這個階段查詢到的映射主機,以此類推 request.getContext()、 request.getWrapper() 都是。
  7. connector.getService().getContainer().getPipeline().getFirst().invoke() 會將請求傳遞到 Container 處理,固然了 Container 處理也是在 Worker 線程中執行的,可是這是一個相對獨立的模塊,因此單獨分出來一節。

Container

  1. 須要注意的是,基本上每個容器的 StandardPipeline 上都會有多個已註冊的 Valve,咱們只關注每一個容器的 Basic Valve。其餘 Valve 都是在 Basic Valve 前執行。
  2. request.getHost().getPipeline().getFirst().invoke() 先獲取對應的 StandardHost,並執行其 pipeline。
  3. request.getContext().getPipeline().getFirst().invoke() 先獲取對應的 StandardContext, 並執行其 pipeline。
  4. request.getWrapper().getPipeline().getFirst().invoke() 先獲取對應的 StandardWrapper,並執行其 pipeline。
  5. 最值得說的就是 StandardWrapper 的 Basic Valve, StandardWrapperValve
  6. allocate() 用來加載並初始化 Servlet,值的一提的是 Servlet 並不都是單例的,當 Servlet 實現了 SingleThreadModel 接口後, StandardWrapper 會維護一組 Servlet 實例,這是享元模式。固然了 SingleThreadModel 在 Servlet 2.4 之後就棄用了。
  7. createFilterChain() 方法會從 StandardContext 中獲取到全部的過濾器,而後將匹配 Request URL 的全部過濾器挑選出來添加到 filterChain 中。
  8. doFilter() 執行過濾鏈, 當全部的過濾器都執行完畢後調用 Servlet 的 service() 方法。

Reference

  1. 《How Tomcat works》
  2. 《Tomcat 架構解析》-- 劉光瑞
  3. Tomcat-9.0-doc
  4. apache-tomcat-9.0.0.M22-src
  5. tomcat 架構分析 (connector NIO 實現)

最後

歡迎你們關注個人公衆號【程序員追風】,文章都會在裏面更新,整理的資料也會放在裏面。
相關文章
相關標籤/搜索