Tomcat原理系列之三:對請求的過程詳細分析

請求的處理是整個Tomcat的核心。深刻了解Tomcat的請求過程,對於咱們理解咱們的應用項目,對於咱們解決問題,對於咱們從此開發項目都有深遠的影響java

若是看過Tomcat原理系列之二:由點到線,請求主幹;必定對請求鏈具體走了哪些組件有了印象。咱們再進一步拆解請求鏈,將鏈上涉及的類一一道來。app

Connector

Connector組件做爲Server的一部分,主要用於接收,解析http請求,並將請求封裝成requset交給Container容器進行處理. Connector是如何接受請求的呢?框架

Connector使用ProtocolHandler(協議處理器)來處理請求的, 不一樣的ProtocolHandler處理不一樣模式的請求類型. 例如: Http11Protocol支持BIO類型的Socket來鏈接的, Http11NioProtocol支持NIO類型的NioSocket來鏈接的 ((基於Tomcat8)由於Tomcat高版本默認使用NIO模式,本文以NIO類型的處理來說) socket

在這裏插入圖片描述

Http11NioProtocol:

經過Http11NioProtocol的構造方法. 咱們能夠看出,主要包括兩大部分: Endpoint:端點, 也就是socket的請求的入口. ConnectionHandler: Connection 處理器ide

public Http11NioProtocol() {
        endpoint=new NioEndpoint();//端點
        cHandler = new Http11ConnectionHandler(this);//connection處理器
        ((NioEndpoint) endpoint).setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }
複製代碼

1.Endpoint的NIO實現NioEndpoint

NioEndpoint是整個Tomcat的請求的入口. NioEndponit類中有個幾個內部類很是重要,也是請求的必經之地.咱們按照請求的走過的順序一個個的解開看. oop

內部類
(不一樣版本的Tomcat可能有所不一樣,可是基本框架是相似的.)

1. Acceptor線程:

接收socket線程. Acceptor自己就是一個線程,run()方法裏有while循環執行 serverSock.accept()接收線程. 此處有一個點要注意, 雖然是基於NIO的Endpoint可是這裏仍是阻塞式接收socket鏈接的方法. 也就是說會阻塞到serverSock.accept();
(1)當獲取到SocketChannel對象後, 調用setSocketOptions(socket)方法, 將SocketChannel封裝到NioChannel對象中. (2)並調用Poller的register(Niochannel socket)方法,將NioChannel進一步封裝到NioSocketWrapper對象中, (3)最後在將NioSocketWrapper對象封裝到PollerEvent對象壓到Pollerevents隊列裏中去.學習

這裏是一個典型的生產-消費者模式Acceptor 是events queue的生產者, Poller是envets queue的消費者.this

(代碼有刪減,只把重要的提取出來)spa

@Override
       public void run() {
           while (running) {
                   try {
                       //接收線程(沒有鏈接時阻塞在此處)
                       socket = serverSock.accept();
                   } catch (IOException ioe) {
                   }
                   // Successful accept, reset the error delay
                   errorDelay = 0;

                   // Configure the socket
                   if (running && !paused) {
                       // setSocketOptions()======= 對socket進一步處理
                       if (!setSocketOptions(socket)) {
                           closeSocket(socket);
                       }
                   } else {
                       closeSocket(socket);
                   }
               } catch (Throwable t) {
                   ExceptionUtils.handleThrowable(t);
                   log.error(sm.getString("endpoint.accept.fail"), t);
               }
           }
           state = AcceptorState.ENDED;
       }
複製代碼
2. Poller線程:

主要用於以較少的資源輪詢已鏈接套接字以保持鏈接,當數據可用時轉給worker工做線程. Poller線程run方法,while循環消費events queue裏的PollerEvent事件 . 經過 event()方不斷取出PollerEvent對象,而後將PollerEvent對象中的NioSocketWrapper包裝類OP_READ事件註冊到Poller的Selector選擇器去.{此處咱們才真正看到NIO的影子} 在註冊完OP_READ事件後. 緊接着執行**selector.selectedKeys()**方法將就緒的通道key返回,而後經過selectedKey訪問就緒的通道. 取出NioSocketWrapper對象. 接着調用Poller的processKey()方法,根據sk是OPEN_READ事件或者OPEN_WRITE事件,調用NioEndpoint.processSocket()方法將NioSocketWrapper封裝到SocketProcessor對象中, 並提交到NioEndpoint.executor線程池(Worker線程池)去執行.net

@Override
       public void run() {
           // Loop until destroy() is called
           while (true) {

               boolean hasEvents = false;

               try {
                   if (!close) {
                       hasEvents = events();
                       if (wakeupCounter.getAndSet(-1) > 0) {
                           //if we are here, means we have other stuff to do
                           //do a non blocking select
                           keyCount = selector.selectNow();
                       } else {
                           keyCount = selector.select(selectorTimeout);
                       }
                       wakeupCounter.set(0);
                   }
                   if (close) {
                       events();
                       timeout(0, false);
                       try {
                           selector.close();
                       } catch (IOException ioe) {
                           log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                       }
                       break;
                   }
               } catch (Throwable x) {
                   ExceptionUtils.handleThrowable(x);
                   log.error("",x);
                   continue;
               }
               //either we timed out or we woke up, process events first
               if ( keyCount == 0 ) hasEvents = (hasEvents | events());

               Iterator<SelectionKey> iterator =
                   keyCount > 0 ? selector.selectedKeys().iterator() : null;
               // Walk through the collection of ready keys and dispatch
               // any active event.
               while (iterator != null && iterator.hasNext()) {
                   SelectionKey sk = iterator.next();
                   NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
                   // Attachment may be null if another thread has called
                   // cancelledKey()
                   if (attachment == null) {
                       iterator.remove();
                   } else {
                       iterator.remove();
                       processKey(sk, attachment);
                   }
               }//while

               //process timeouts
               timeout(keyCount,hasEvents);
           }//while

           getStopLatch().countDown();
       }
複製代碼
3.Worker線程組:

SocketProcessor處理socket鏈接的任務提交到worker線程池去執行

public boolean processSocket(SocketWrapperBase<S> socketWrapper, SocketEvent event, boolean dispatch) {
       try {
           if (socketWrapper == null) {
               return false;
           }
           SocketProcessorBase<S> sc = processorCache.pop();
           if (sc == null) {
               sc = createSocketProcessor(socketWrapper, event);
           } else {
               sc.reset(socketWrapper, event);
           }
           Executor executor = getExecutor();
           if (dispatch && executor != null) {
               executor.execute(sc);//將socket處理任務提交到worker線程池
           } else {
               sc.run();
           }
       } catch (RejectedExecutionException ree) {
           getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
           return false;
       } catch (Throwable t) {
           ExceptionUtils.handleThrowable(t);
           // This means we got an OOM or similar creating a thread, or that
           // the pool and its queue are full
           getLog().error(sm.getString("endpoint.process.fail"), t);
           return false;
       }
       return true;
   }
複製代碼
4.SocketProcessor類:

NioEndpoint.SocketProcessor類做爲一個任務, run()方法中,將socket的包裝類NioEndpoint.NioSocketWrapper交給ProtocolHandler協議處理的ConnectionHandler進行處理

2.ConnectionHandler鏈接處理器

NioSocketWrapper 此時來到 ConnectionHandler這裏.

Http11Processor Http協議處理器(http協議實現)

ConnectionHandler.process()方法,首先獲得一個Http11Processor 處理器, 而後會將NioSocketWrapper交給Http11Processor 的進行處理

在這裏咱們回顧一個經典的問題:HTTP協議的組成部分,或者叫作HTTP協議的報文組成 1.請求行 2.請求頭 3.空行 4.消息體

在這裏插入圖片描述

Http11Processor.service()方法中就是HTTP協議實現的地方. Http11Processor會建立一個Http11InputBuffer對象. Http11InputBuffer中的parseRequestLine、parseHeaders 和 parseHeader方法 會分別讀取[請求行,請求頭]

請求的讀取去哪裏了呢?? 對請求體的讀取卻不在這裏. 而是將請求體的解析與讀取延遲到Servlet中去了. 在調用getParameter,getParameterMap,getParameterNames,getParameterValues時會先調用parseParameters()方法解析請求參數. 這個放到之後再說.

Http11Processor的父類在初始化的時候,會建立Request對象,Response對象. 解析完請求行,請求頭後. NioSocketWrapper對象此時變成了Request對象. 而後就開始下一程.

在這裏插入圖片描述

CoyoteAdapter:(Request,Response)

Http11Processor.service()方法處理後, 會調用CoyoteAdapter.service(request,repose) 對request,repose作一些預處理後,又開始了下一程connector.getService().getContainer().getPipeline().getFirst().invoke( request, response);

Container:(Request,Response)

Request,Response 對象層層通過Engine,Host,Context,Wrapper的valve.

Valve

Valve做爲一個個基礎的閥門,扮演着業務實際執行者的角色.

在這裏插入圖片描述

1.StandardWrapperValve(最後一個valve)

在StandardWrapperValve.invoke方法中會根據配置的Filter過濾器建立ApplicationFilterChain過濾器鏈.

ApplicationFilterChain filterChain =
              ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

複製代碼

而後執行過濾器鏈的doFilter方法

filterChain.doFilter(request.getRequest(),
                                  response.getResponse());
複製代碼
2.ApplicationFilterChain

ApplicationFilterChain使用責任了模式,執行過濾器的doFilter方法. 當執行完最後一個Filter方法後. 調用

servlet.service(request, response);
複製代碼

此時到了咱們常見的servlet了.終於到了咱們的業務代碼了

總結:

socket ----->Connector接收socket鏈接封裝成NioSocketWrapper對象 ----->ConnectionHandler取得HTTP協議內容,建立Requset,Reponse對象 ----->Container攜帶Requset,Reponse對象層級執行valve的invoke方法,到達最後一個StandardWrapperValve, 建立ApplicationFilterChain過濾器鏈, ----->ApplicationFilterChain過濾器鏈執行doFilter方法,執行完成後調用servlet.service()方法 ----->業務代碼

我的理解有偏差,望指出. Tomcat中涉及的細節不少.須要慢慢品味. 可是請求的大致行動路線,就如上描述的那樣. 但願讀者不斷的去看源碼挖掘更深的細節. 學習Tomact優秀的設計

相關文章
相關標籤/搜索