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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

谈谈 Tomcat 请求处理流程

發布時間:2025/3/21 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 谈谈 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。

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() 方法。

  • Reference

  • 《How Tomcat works》

    https://www.amazon.com/How-Tomcat-Works-Budi-Kurniawan/dp/097521280X

  • 《Tomcat 架構解析》– 劉光瑞

    http://product.dangdang.com/25084132.html

  • Tomcat-9.0-doc

    https://tomcat.apache.org/tomcat-9.0-doc/index.html

  • apache-tomcat-9.0.0.M22-src

    http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.0.M22/src/

  • tomcat架構分析 (connector NIO 實現)

    http://gearever.iteye.com/blog/1844203

  • 總結

    以上是生活随笔為你收集整理的谈谈 Tomcat 请求处理流程的全部內容,希望文章能夠幫你解決所遇到的問題。

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