Tomcat源碼學習第4篇 - Servlet請求分析

前段時間家裏有事忙,停更了好長一段時間,這裏跟等待更新的小夥伴們說一聲抱歉,沒能提早說明一下,讓小夥伴們等了這麼久,真的很差意思!

前面說完了Tomcat的初始化和啓動步驟,那麼接下來就要進入重頭戲了!在本篇文章中,我會跟前面同樣,經過圖文的方式來帶着小夥伴們瞭解一個 Servlet是如何被tomcat處理的,具體的處理鏈路都有哪些。java

1、請求分析

在《Tomcat源碼學習第2篇》中備註了各個組件的說明。git

當一個servlet請求到來的時候,首先通過的是connector組件,它是用來接收請求的。github

該組件接收到請求以後,會把相關請求進行封裝,而後傳遞到engine組件中。緩存

緊跟着,engine組件會鎖定對應的hostcontext以及wrapper,一層層的傳遞下去,找到最終處理請求的servlet實例。tomcat

請求鏈路

2、深刻探索

不知道你們還有沒有印象,在前面的文章中,咱們在NioEndpoint類中,啓動Accepter線程的入口處上方還有着一個線程組在啓動運行,然而卻沒有講解該線程是用來幹嗎的~app

NioEndpoint.startInternal()

NioEndpoint.startInternal()

點擊跳轉到該類過來,咱們能夠看到他實現了Runnable接口,那麼咱們直接查看他的run()方法,看看它的運行邏輯。socket

Poller.run()

Poller

經過註釋咱們能夠知道,該線程主要用於輪詢已鏈接的套接字,檢查是否觸發了事件,並在事件發生時將關聯的套接字移交給對應的處理器。在源碼中咱們能夠看到keyCount變量記錄着待處理請求數,提供給後面作相應判斷。ide

Poller.run()

繼續往下走,經過keyCount判斷是否有請求須要進行處理,須要的話則經過selector.selectedKeys()拿到須要被處理的channel集合,進行循環處理。在while循環中咱們看到,全部就緒的通道都調用的是processKey(sk, socketWrapper)方法進行處理。post

image-20210503200131828

點擊跳轉過來該方法,在這裏能夠看到他對該sk作了讀寫判斷,既然是請求進來,那確定是作讀操做,咱們先進讀相關的方法看一下。學習

NioEndpoint.processKey()

NioEndpoint.processKey()

進來以後咱們能夠看到它首先在緩存池中嘗試去獲取一個處理線程,當緩存池中沒有線程時,就建立一個新的線程,若是有的話就直接使用。

AbstractEndpoint.processSocket()

AbstractEndpoint.processSocket()

既然是線程了,那麼咱們就關心線程的核心方法便可。點擊SocketProcessorBase跳轉查看run()方法。

SocketProcessorBase.run()

SocketProcessorBase.run()

doRun()處打上斷點,單擊下一步,跳轉到NioEndpoint.doRun()方法中。Poller線程移交到這邊的線程進行處理,在該線程中須要獲得當前的socket,作進一步的處理。

NioEndpoint.doRun()

image-20210503212817648

進入該方法以後,咱們能夠看到它首先對wrapper進行判斷,不爲空再取出socket,而後嘗試着在connections中去獲取對應的processor,若是獲取不到,再嘗試獲取已經處理過鏈接,可是還沒有銷燬的processor中去獲取,還獲取不到才進行建立。這樣能夠避免頻繁的建立和銷燬對象。

AbstractProtocol.process()

AbstractProtocol.process()

AbstractProtocol.process()

獲得processor以後,調用process方法對報文進行解析。

AbstractProtocol.process()

進入該方法以後,咱們能夠看到這裏面是對socketEvent的狀態進行判斷,咱們當前請求主要是讀狀態,在此處打上斷點,跳到該方法進來看一下。

AbstractProcessorLight.process()

AbstractProcessorLight.process()

這裏咱們能夠看到是進入到了 http11類中,在該類裏面對報文進行解析,封裝原生的requestresponse對象。這裏的response由於咱們尚未到返回的步驟,因此只是作個初步的參數設置。後續要傳入Adapter進行下一步操做。

Http11Processor.service()

Http11Processor.service()

Http11Processor.service()

Http11Processor.service()

在這裏對原生的requestresponse進行轉換,獲得HttpServletRequestHttpServletResponse。而後根據請求信息找到可以處理當前請求的hostcontextwrapper

CoyoteAdapter.service()

CoyoteAdapter.service()

在這方法能夠看到它會經過getMapper()方法去匹配可以處理當前請求的 host,context,wrapper。到這裏可能有的小夥伴會奇怪,爲何是從mapper中去匹配呢?這個問題留給大家去探索一下,等下篇再給大家解答。

CoyoteAdapter.postParseRequest()

CoyoteAdapter.postParseRequest()

上一方法中,經過connector獲取service以後再取得對應的mapper,但是進來以後卻沒有看到對該mapper對象的構建,那該對象是哪裏來的呢?

Mapper.map()

Mapper.map()

不知道你們還記不記得在第二篇中,在StandardService類中initInternal()startInternal()方法中有mapperListener方法的初始化和啓動。

StandardService.initInternal()

StandardService.startInternal()

在該方法中查找到對應的host, context, wrapper

Mapper.internalMap()

Mapper.internalMap()

Mapper.internalMap()

回到CoyoteAdapter.postParseRequest(),經過Evaluste咱們能夠看到當前請求對應的host, context, wrapper以及實例的映射均已找到。

CoyoteAdapter.postParseRequest()

接下來要作的就是根據鏈路組件進行一級級的調用,直至最後取出servlet執行。

CoyoteAdapter.service()

CoyoteAdapter.service()

先獲得host,在經過host繼續調用下一級組件

StandardEngineValve.invoke()

StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

ErrorReportValve.invoke()

這裏拿到context,繼續invoke()

StandardHostValve.invoke()

StandardHostValve.invoke()

AuthenticatorBase.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

StandardContextValve.invoke()

拿到wrapper以後,繼續向下執行,從wrapper容器中獲得servlet對象。

StandardWrapperValve.invoke()

StandardWrapperValve.invoke()

緊接着,把獲得的servlet加入過濾器鏈中(可能有其它的處理,這裏不直接進行處理),留待下面調用過濾器鏈再統一進行處理。

StandardWrapperValve.invoke()

StandardWrapperValve.invoke()

ApplicationFilterChain.doFilter()

ApplicationFilterChain.doFilter()

終於找到具體的實例了,太不容易了!!!

ApplicationFilterChain.internalDoFilter()

ApplicationFilterChain.internalDoFilter()

3、總結

Servlet請求鏈路

我收集有衆多的 計算機電子書籍,有須要的小夥伴自提哦~
相關文章
相關標籤/搜索