<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3801字,需瀏覽 8分鐘

           ·

          2021-05-04 22:38

          前段時間家里有事忙,停更了好長一段時間,這里跟等待更新的小伙伴們說一聲抱歉,沒能提前說明一下,讓小伙伴們等了這么久,真的不好意思!

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

          一、請求分析

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

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

          該組件接收到請求之后,會把相關請求進行封裝,然后傳遞到engine組件中。

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

          請求鏈路

          二、深入探索

          不知道大家還有沒有印象,在前面的文章中,我們在NioEndpoint類中,啟動Accepter線程的入口處上方還有著一個線程組在啟動運行,然而卻沒有講解該線程是用來干嘛的~

          NioEndpoint.startInternal()

          NioEndpoint.startInternal()

          點擊跳轉到該類過來,我們可以看到他實現了Runnable接口,那么我們直接查看他的run()方法,看看它的運行邏輯。

          Poller.run()

          Poller

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

          Poller.run()

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

          image-20210503200131828

          點擊跳轉過來該方法,在這里可以看到他對該sk做了讀寫判斷,既然是請求進來,那肯定是做讀操作,我們先進讀相關的方法看一下。

          NioEndpoint.processKey()

          NioEndpoint.processKey()

          進來之后我們可以看到它首先在緩存池中嘗試去獲取一個處理線程,當緩存池中沒有線程時,就創(chuàng)建一個新的線程,如果有的話就直接使用。

          AbstractEndpoint.processSocket()

          AbstractEndpoint.processSocket()

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

          SocketProcessorBase.run()

          SocketProcessorBase.run()

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

          NioEndpoint.doRun()
          image-20210503212817648

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

          AbstractProtocol.process()

          AbstractProtocol.process()
          AbstractProtocol.process()

          得到processor之后,調用process方法對報文進行解析。

          AbstractProtocol.process()

          進入該方法之后,我們可以看到這里面是對socketEvent的狀態(tài)進行判斷,我們當前請求主要是讀狀態(tài),在此處打上斷點,跳到該方法進來看一下。

          AbstractProcessorLight.process()

          AbstractProcessorLight.process()

          這里我們可以看到是進入到了 http11類中,在該類里面對報文進行解析,封裝原生的requestresponse對象。這里的response因為我們還沒有到返回的步驟,所以只是做個初步的參數設置。后續(xù)要傳入Adapter進行下一步操作。

          Http11Processor.service()

          Http11Processor.service()
          Http11Processor.service()
          Http11Processor.service()

          在這里對原生的requestresponse進行轉換,得到HttpServletRequestHttpServletResponse。然后根據請求信息找到能夠處理當前請求的host,context,wrapper

          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執(zhí)行。

          CoyoteAdapter.service()

          CoyoteAdapter.service()

          先得到host,在通過host繼續(xù)調用下一級組件

          StandardEngineValve.invoke()

          StandardEngineValve.invoke()

          AbstractAccessLogValve.invoke()

          AbstractAccessLogValve.invoke()

          ErrorReportValve.invoke()

          ErrorReportValve.invoke()

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

          StandardHostValve.invoke()

          StandardHostValve.invoke()

          AuthenticatorBase.invoke()

          AuthenticatorBase.invoke()

          StandardContextValve.invoke()

          StandardContextValve.invoke()

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

          StandardWrapperValve.invoke()

          StandardWrapperValve.invoke()

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

          StandardWrapperValve.invoke()
          StandardWrapperValve.invoke()

          ApplicationFilterChain.doFilter()

          ApplicationFilterChain.doFilter()

          終于找到具體的實例了,太不容易了?。?!

          ApplicationFilterChain.internalDoFilter()

          ApplicationFilterChain.internalDoFilter()

          三、總結

          Servlet請求鏈路

          我收集有眾多的 計算機電子書籍,有需要的小伙伴自提哦~

          - END -


          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产黄片手机在线观看 | 大香蕉高清影院 | 东京热无码视频 | 中文字幕在线网址 | 亚洲一日韩一欧美一级A片么 |