Tomcat源碼學(xué)習(xí)第4篇 - Servlet請(qǐng)求分析
前段時(shí)間家里有事忙,停更了好長(zhǎng)一段時(shí)間,這里跟等待更新的小伙伴們說(shuō)一聲抱歉,沒(méi)能提前說(shuō)明一下,讓小伙伴們等了這么久,真的不好意思!
前面說(shuō)完了Tomcat的初始化和啟動(dòng)步驟,那么接下來(lái)就要進(jìn)入重頭戲了!在本篇文章中,我會(huì)跟前面一樣,通過(guò)圖文的方式來(lái)帶著小伙伴們了解一個(gè) Servlet是如何被tomcat處理的,具體的處理鏈路都有哪些。
一、請(qǐng)求分析
在《Tomcat源碼學(xué)習(xí)第2篇》中備注了各個(gè)組件的說(shuō)明。
當(dāng)一個(gè)servlet請(qǐng)求到來(lái)的時(shí)候,首先經(jīng)過(guò)的是connector組件,它是用來(lái)接收請(qǐng)求的。
該組件接收到請(qǐng)求之后,會(huì)把相關(guān)請(qǐng)求進(jìn)行封裝,然后傳遞到engine組件中。
緊跟著,engine組件會(huì)鎖定對(duì)應(yīng)的host,context以及wrapper,一層層的傳遞下去,找到最終處理請(qǐng)求的servlet實(shí)例。

二、深入探索
不知道大家還有沒(méi)有印象,在前面的文章中,我們?cè)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">NioEndpoint類(lèi)中,啟動(dòng)Accepter線(xiàn)程的入口處上方還有著一個(gè)線(xiàn)程組在啟動(dòng)運(yùn)行,然而卻沒(méi)有講解該線(xiàn)程是用來(lái)干嘛的~
NioEndpoint.startInternal()

點(diǎn)擊跳轉(zhuǎn)到該類(lèi)過(guò)來(lái),我們可以看到他實(shí)現(xiàn)了Runnable接口,那么我們直接查看他的run()方法,看看它的運(yùn)行邏輯。
Poller.run()

通過(guò)注釋我們可以知道,該線(xiàn)程主要用于輪詢(xún)已連接的套接字,檢查是否觸發(fā)了事件,并在事件發(fā)生時(shí)將關(guān)聯(lián)的套接字移交給對(duì)應(yīng)的處理器。在源碼中我們可以看到keyCount變量記錄著待處理請(qǐng)求數(shù),提供給后面做相應(yīng)判斷。

繼續(xù)往下走,通過(guò)keyCount判斷是否有請(qǐng)求需要進(jìn)行處理,需要的話(huà)則通過(guò)selector.selectedKeys()拿到需要被處理的channel集合,進(jìn)行循環(huán)處理。在while循環(huán)中我們看到,所有就緒的通道都調(diào)用的是processKey(sk, socketWrapper)方法進(jìn)行處理。

點(diǎn)擊跳轉(zhuǎn)過(guò)來(lái)該方法,在這里可以看到他對(duì)該sk做了讀寫(xiě)判斷,既然是請(qǐng)求進(jìn)來(lái),那肯定是做讀操作,我們先進(jìn)讀相關(guān)的方法看一下。
NioEndpoint.processKey()

進(jìn)來(lái)之后我們可以看到它首先在緩存池中嘗試去獲取一個(gè)處理線(xiàn)程,當(dāng)緩存池中沒(méi)有線(xiàn)程時(shí),就創(chuàng)建一個(gè)新的線(xiàn)程,如果有的話(huà)就直接使用。
AbstractEndpoint.processSocket()

既然是線(xiàn)程了,那么我們就關(guān)心線(xiàn)程的核心方法即可。點(diǎn)擊SocketProcessorBase跳轉(zhuǎn)查看run()方法。
SocketProcessorBase.run()

在doRun()處打上斷點(diǎn),單擊下一步,跳轉(zhuǎn)到NioEndpoint.doRun()方法中。Poller線(xiàn)程移交到這邊的線(xiàn)程進(jìn)行處理,在該線(xiàn)程中需要得到當(dāng)前的socket,做進(jìn)一步的處理。


進(jìn)入該方法之后,我們可以看到它首先對(duì)wrapper進(jìn)行判斷,不為空再取出socket,然后嘗試著在connections中去獲取對(duì)應(yīng)的processor,如果獲取不到,再?lài)L試獲取已經(jīng)處理過(guò)連接,但是尚未銷(xiāo)毀的processor中去獲取,還獲取不到才進(jìn)行創(chuàng)建。這樣可以避免頻繁的創(chuàng)建和銷(xiāo)毀對(duì)象。
AbstractProtocol.process()


得到processor之后,調(diào)用process方法對(duì)報(bào)文進(jìn)行解析。

進(jìn)入該方法之后,我們可以看到這里面是對(duì)socketEvent的狀態(tài)進(jìn)行判斷,我們當(dāng)前請(qǐng)求主要是讀狀態(tài),在此處打上斷點(diǎn),跳到該方法進(jìn)來(lái)看一下。
AbstractProcessorLight.process()

這里我們可以看到是進(jìn)入到了 http11類(lèi)中,在該類(lèi)里面對(duì)報(bào)文進(jìn)行解析,封裝原生的request和response對(duì)象。這里的response因?yàn)槲覀冞€沒(méi)有到返回的步驟,所以只是做個(gè)初步的參數(shù)設(shè)置。后續(xù)要傳入Adapter進(jìn)行下一步操作。
Http11Processor.service()



在這里對(duì)原生的request和response進(jìn)行轉(zhuǎn)換,得到HttpServletRequest和HttpServletResponse。然后根據(jù)請(qǐng)求信息找到能夠處理當(dāng)前請(qǐng)求的host,context,wrapper。
CoyoteAdapter.service()

在這方法可以看到它會(huì)通過(guò)getMapper()方法去匹配能夠處理當(dāng)前請(qǐng)求的 host,context,wrapper。到這里可能有的小伙伴會(huì)奇怪,為什么是從mapper中去匹配呢?這個(gè)問(wèn)題留給你們?nèi)ヌ剿饕幌?,等下篇再給你們解答。
CoyoteAdapter.postParseRequest()

上一方法中,通過(guò)connector獲取service之后再取得對(duì)應(yīng)的mapper,可是進(jìn)來(lái)之后卻沒(méi)有看到對(duì)該mapper對(duì)象的構(gòu)建,那該對(duì)象是哪里來(lái)的呢?
Mapper.map()

不知道大家還記不記得在第二篇中,在StandardService類(lèi)中initInternal()和startInternal()方法中有mapperListener方法的初始化和啟動(dòng)。


在該方法中查找到對(duì)應(yīng)的host, context, wrapper。
Mapper.internalMap()


回到CoyoteAdapter.postParseRequest(),通過(guò)Evaluste我們可以看到當(dāng)前請(qǐng)求對(duì)應(yīng)的host, context, wrapper以及實(shí)例的映射均已找到。

接下來(lái)要做的就是根據(jù)鏈路組件進(jìn)行一級(jí)級(jí)的調(diào)用,直至最后取出servlet執(zhí)行。
CoyoteAdapter.service()

先得到host,在通過(guò)host繼續(xù)調(diào)用下一級(jí)組件
StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

這里拿到context,繼續(xù)invoke()。
StandardHostValve.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

拿到wrapper之后,繼續(xù)向下執(zhí)行,從wrapper容器中得到servlet對(duì)象。
StandardWrapperValve.invoke()

緊接著,把得到的servlet加入過(guò)濾器鏈中(可能有其它的處理,這里不直接進(jìn)行處理),留待下面調(diào)用過(guò)濾器鏈再統(tǒng)一進(jìn)行處理。


ApplicationFilterChain.doFilter()

終于找到具體的實(shí)例了,太不容易了?。。?/p>
ApplicationFilterChain.internalDoFilter()

三、總結(jié)

- END -我收集有眾多的 計(jì)算機(jī)電子書(shū)籍,有需要的小伙伴自提哦~
