來源:github.com/c-rainstorm/blog/blob/master/tomcat/
很多東西在時序圖中體現的已經非常清楚了,沒有必要再一步一步的作介紹,所以本文以圖為主,然后對部分內容加以簡單解釋,
繪制圖形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension
本文對 Tomcat 的介紹以 Tomcat-9.0.0.M22 為標準,
https://tomcat.apache.org/tomcat-9.0-doc/index.html
Overview

Connector 啟動以后會啟動一組執行緒用于不同階段的請求處理程序,
- Acceptor 執行緒組,用于接受新連接,并將新連接封裝一下,選擇一個 Poller 將新連接添加到 Poller 的事件佇列中,
- Poller 執行緒組,用于監聽 Socket 事件,當 Socket 可讀或可寫等等時,將 Socket 封裝一下添加到 worker 執行緒池的任務佇列中,
- worker 執行緒組,用于對請求進行處理,包括分析請求報文并創建 Request 物件,呼叫容器的 pipeline 進行處理,
Acceptor、Poller、worker 所在的 ThreadPoolExecutor 都維護在 NioEndpoint 中,
Connector Init and Start

- initServerSocket(),通過 ServerSocketChannel.open() 打開一個 ServerSocket,默認系結到 8080 埠,默認的連接等待佇列長度是 100, 當超過 100 個時會拒絕服務,我們可以通過配置 conf/server.xml 中 Connector 的 acceptCount 屬性對其進行定制,
- createExecutor() 用于創建 Worker 執行緒池,默認會啟動 10 個 Worker 執行緒,Tomcat 處理請求程序中,Woker 最多不超過 200 個,我們可以通過配置 conf/server.xml 中 Connector 的 minSpareThreads 和 maxThreads 對這兩個屬性進行定制,
- Pollor 用于檢測已就緒的 Socket, 默認最多不超過 2 個,Math.min(2,Runtime.getRuntime().availableProcessors());,我們可以通過配置 pollerThreadCount 來定制,
- Acceptor 用于接受新連接,默認是 1 個,我們可以通過配置 acceptorThreadCount 對其進行定制,
Requtst Process
Acceptor

- Acceptor 在啟動后會阻塞在 ServerSocketChannel.accept(); 方法處,當有新連接到達時,該方法回傳一個 SocketChannel,
- 配置完 Socket 以后將 Socket 封裝到 NioChannel 中,并注冊到 Poller,值的一提的是,我們一開始就啟動了多個 Poller 執行緒,注冊的時候,連接是公平的分配到每個 Poller 的,NioEndpoint 維護了一個 Poller 陣列,當一個連接分配給 pollers[index] 時,下一個連接就會分配給 pollers[(index+1)%pollers.length].
- addEvent() 方法會將 Socket 添加到該 Poller 的 PollerEvent 佇列中,到此 Acceptor 的任務就完成了,
Poller

- selector.select(1000),當 Poller 啟動后因為 selector 中并沒有已注冊的 Channel,所以當執行到該方法時只能阻塞,所有的 Poller 共用一個 Selector,其實作類是 sun.nio.ch.EPollSelectorImpl
- events() 方法會將通過 addEvent() 方法添加到事件佇列中的 Socket 注冊到 EPollSelectorImpl,當 Socket 可讀時,Poller 才對其進行處理
- createSocketProcessor() 方法將 Socket 封裝到 SocketProcessor 中,SocketProcessor 實作了 Runnable 介面,worker 執行緒通過呼叫其 run() 方法來對 Socket 進行處理,
- execute(SocketProcessor) 方法將 SocketProcessor 提交到執行緒池,放入執行緒池的 workQueue 中,workQueue 是 BlockingQueue 的實體,到此 Poller 的任務就完成了,
Worker

- worker 執行緒被創建以后就執行 ThreadPoolExecutor 的 runWorker() 方法,試圖從 workQueue 中取待處理任務,但是一開始 workQueue 是空的,所以 worker 執行緒會阻塞在 workQueue.take() 方法,
- 當新任務添加到 workQueue后,workQueue.take() 方法會回傳一個 Runnable,通常是 SocketProcessor,然后 worker 執行緒呼叫 SocketProcessor 的 run() 方法對 Socket 進行處理,
- createProcessor() 會創建一個 Http11Processor, 它用來決議 Socket,將 Socket 中的內容封裝到 Request 中,注意這個 Request 是臨時使用的一個類,它的全類名是 org.apache.coyote.Request,
- postParseRequest() 方法封裝一下 Request,并處理一下映射關系(從 URL 映射到相應的 Host、Context、Wrapper),
- CoyoteAdapter 將 Rquest 提交給 Container 處理之前,并將 org.apache.coyote.Request 封裝到 org.apache.catalina.connector.Request,傳遞給 Container 處理的 Request 是 org.apache.catalina.connector.Request,
- connector.getService().getMapper().map(),用來在 Mapper 中查詢 URL 的映射關系,映射關系會保留到 org.apache.catalina.connector.Request 中,Container 處理階段 request.getHost() 是使用的就是這個階段查詢到的映射主機,以此類推 request.getContext()、request.getWrapper() 都是,
- connector.getService().getContainer().getPipeline().getFirst().invoke() 會將請求傳遞到 Container 處理,當然了 Container 處理也是在 Worker 執行緒中執行的,但是這是一個相對獨立的模塊,所以單獨分出來一節,
Container

- 需要注意的是,基本上每一個容器的 StandardPipeline 上都會有多個已注冊的 Valve,我們只關注每個容器的 Basic Valve,其他 Valve 都是在 Basic Valve 前執行,
- request.getHost().getPipeline().getFirst().invoke() 先獲取對應的 StandardHost,并執行其 pipeline,
- request.getContext().getPipeline().getFirst().invoke() 先獲取對應的 StandardContext,并執行其 pipeline,
- request.getWrapper().getPipeline().getFirst().invoke() 先獲取對應的 StandardWrapper,并執行其 pipeline,
- 最值得說的就是 StandardWrapper 的 Basic Valve,StandardWrapperValve
- allocate() 用來加載并初始化 Servlet,值的一提的是 Servlet 并不都是單例的,當 Servlet 實作了 SingleThreadModel 介面后,StandardWrapper 會維護一組 Servlet 實體,這是享元模式,當然了 SingleThreadModel在 Servlet 2.4 以后就棄用了,
- createFilterChain() 方法會從 StandardContext 中獲取到所有的過濾器,然后將匹配 Request URL 的所有過濾器挑選出來添加到 filterChain 中,
- doFilter() 執行過濾鏈,當所有的過濾器都執行完畢后呼叫 Servlet 的 service() 方法,
參考:
https://www.amazon.com/How-Tomcat-Works-Budi-Kurniawan/dp/097521280X
http://product.dangdang.com/25084132.html
https://tomcat.apache.org/tomcat-9.0-doc/index.html
http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.0.M22/src/
http://gearever.iteye.com/blog/1844203
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2021最新版)
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.阿里 Mock 工具正式開源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式發布,全新顛覆性版本!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/288310.html
標籤:其他
下一篇:java 發郵件 代碼
