Tomcat處理請求的流程
在Tomcat處理客戶端請求的過程中,這里面有三個組件概念,他們都是線程,分別負責不同的職責。(必須記清楚這三個線程組件)
Acceptor
一個普通線程任務,用于接收新連接,并將新連接封裝,選擇一個 Poller 將新連接添加到 Poller 的事件隊列中。
Poller
一個線程任務,用于監(jiān)聽 Socket 事件,當有任務來臨時,將 Socket 封裝,添加到 worker 線程池的任務隊列中。
Worker
他是創(chuàng)建一組線程的,每個線程任務都是一個阻塞隊列,用于對請求進行處理(例如我們中間件參數(shù)中的最大線程數(shù)就是指最多創(chuàng)建多少個Worker線程)。每個Worker線程任務包括分析解析請求報文、創(chuàng)建 Request 對象、調用容器的 管道pipeline 執(zhí)行閥門value、執(zhí)行servlet的具體邏輯。
二、請求處理流程1.總體流程圖Tomcat啟動時,如果默認使用NIO模式,先是執(zhí)行了AbstractEndpoint.initServerSocket,通過 ServerSocketChannel.open() 打開一個 ServerSocket通道,默認綁定到 8080 端口,用于監(jiān)聽請求。
說明:在Java語言的NIO中,類ServerSocketChannel就是用來處理TPC連接的客戶端,他的open方法就是用例建立一個TPC連接。
然后Tomcat會創(chuàng)建Worker 線程池、Acceptor線程、Poller線程:
AbstractEndpoint.createExecutor,用于創(chuàng)建 Worker 線程池,這個線程池是用來處理實際的請求的,把配置文件中的初始線程數(shù)10、最大線程數(shù)200等信息傳進去,創(chuàng)建一個線程池executor
AbstractEndpoint.createExecutor.startAcceptorThread,他創(chuàng)建一個線程任務Acceptor,作為一個接收者,線程用來無限循環(huán)接受客戶端發(fā)送過來的連接請求
NioEndpoint.startInternal,創(chuàng)建一個線程任務Poller,用于檢測Acceptor接獸并處理成已就緒的 Socket。
Tomcat啟動完成后,客戶端發(fā)起一個請求
Acceptor的run方法,無限循環(huán)在這里接受連接請求(假如啟動后客戶端發(fā)起一個請求,這里就是第一時間捕獲到)
點進去NioEndpoint.serverSocketAccept(因為使用NIO模式),可以看到我們熟悉的nio的accept方法,這是一個阻塞的方法,會一直等待接收請求。
當Acceptor接收到客戶端的請求時,調用addEvent() 方法會將 Socket 添加到該 Poller 的 PollerEvent 隊列中。并調用了NIO中selector.wakeup方法,喚醒了Poller。到此,這一次請求中 Acceptor 的任務就完成了。
接著到Poller 線程了,Poller 線程1秒阻塞一次,等待有請求過來被喚醒后,每次請求先過AbstractEndpoint.processSocket
從處理器緩存中獲取當前要被執(zhí)行的任務,放進任務進程,然后獲取Worker線程組,將這個任務放進去。到此 Poller 的任務就完成了。
然后就是到Worker線程組了,這次請求的后續(xù)的所有操作都在這個線程中完成。Worker線程是一個阻塞隊列,它繼承自AbstractQueuedSynchronizer。worker 線程被創(chuàng)建以后就執(zhí)行 ThreadPoolExecutor 的 runWorker() 方法,試圖從 workQueue 中取待處理任務,但是一開始 workQueue 是空的,所以 worker 線程會阻塞在 workQueue.take() 方法。
當新任務添加到 workQueue后,workQueue.take() 阻塞就會結束,會返回一個 Runnable,通常是 SocketWrapperBase,然后 worker 線程調用 SocketWrapperBase的 run() 方法對 Socket 進行處理。
執(zhí)行SocketWrapperBase.run
里面調用的是 doRun方法,他是抽象方法,根據(jù)當前Tomcat使用的模式是NIO還是APR去選擇執(zhí)行不同的方法(默認是NIO執(zhí)行NioEndpoint里的內部類SocketProcessor.doRun)
這個socket處理器先做TPC的三次握手
三次握手,這里Tomcat作為服務端,是需要響應(執(zhí)行)兩次的,源碼斷點發(fā)現(xiàn)每次http請求這里都是執(zhí)行兩次
三次握手中該方法執(zhí)行兩次:
第一次執(zhí)行時event對象是null,執(zhí)行完是OPEN_READ,表示數(shù)據(jù)可供客戶端讀取
第二次執(zhí)行時event對象是OPEN_READ,如果停用長連接,執(zhí)行完返回的是CLOSE,關閉連接,否則不關閉走到下面,他是獲取協(xié)議處理器,并執(zhí)行他的process方法
執(zhí)行AbstractProtocol.process
可以看到他是獲取的Http11Processor,因為默認用的協(xié)議是http1.1
下面執(zhí)行這個處理器的process方法
跟進去AbstractProcessorLight.process,然后到了Http11Processor.service,service方法先是從當前請求request中,解析請求行、請求頭、請求體,封裝成Request對象
下面獲取adapter(CoyoteAdaptor),調用service方法
執(zhí)行CoyoteAdaptor.service
獲取到Request和Response并封裝
然后調用postParseRequest方法,在 Mapper 中查詢 URL 的映射關系
下面把封裝成的Request對象和響應的Response對象傳遞給Engine容器,然后獲取他的管道,執(zhí)行里面綁定的閥門value
按順序執(zhí)行多個閥門,實現(xiàn)對應的功能
最后執(zhí)行到StandardWrapperValve.invoke
將Servlet封裝到FilterChain過濾器鏈中
他是定位到ApplicationFilterChain.doFilter,里面先是執(zhí)行了Tomcat內置的過濾器
下面執(zhí)行了servlet.service
然后這里就是去調用我們熟悉的HttpServlet的service方法,解析里面對應的doGet方法,或者doPost方法等等… ,也就是執(zhí)行具體業(yè)務方法。
最后由Servlet將響應返回給了客戶端。
到此這篇關于Tomcat處理請求的流程的文章就介紹到這了,更多相關Tomcat處理請求內容請搜索好吧啦網以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
