Tomcat源碼解讀系列(三)——Tomcat對HTTP請求處理的總體流程

    前面的文章中介紹了Tomcat初始化的過程,本文將會介紹Tomcat對HTTP請求的處理的總體流程,更細節的。java

    在上一篇文章中,介紹到JIoEndpoint 中的內部類Acceptor用來接受Socket請求,並調用processSocket方法來進行請求的處理,因此會從本文這個方法開始進行講解。apache

protected boolean processSocket(Socket socket) {
        try {
            if (executor == null) {
                getWorkerThread().assign(socket);
            } else {
                executor.execute(new SocketProcessor(socket));
            }
        } catch (Throwable t) {
            //……此處略去若干代碼
        }
        return true;
}

在以上的代碼中,首先會判斷是否在server.xml配置了進程池,若是配置了的話,將會使用該線程池進行請求的處理,若是沒有配置的話將會使用JIoEndpoint中本身實現的線程池WorkerStack來進行請求的處理,咱們將會介紹WorkerStack的請求處理方式。數據結構

protected Worker getWorkerThread() {
        // Allocate a new worker thread
        synchronized (workers) {
            Worker workerThread;
            while ((workerThread = createWorkerThread()) == null) {
                try {
                    workers.wait();
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
            return workerThread;
        }
}

在以上的代碼中,最終返回了一個Worker的實例,有其來進行請求的處理,在這裏,咱們再看一下createWorkerThread方法,該方法會生成或者在線程池中取到一個線程。app

protected Worker createWorkerThread() {
        synchronized (workers) {
            if (workers.size() > 0) {
            //若是線程池中有空閒的線程,取一個
                curThreadsBusy++;
                return workers.pop();
            }
            if ((maxThreads > 0) && (curThreads < maxThreads)) {
                //若是尚未超過最大線程數,會新建一個線程
                curThreadsBusy++;
                return (newWorkerThread());
            } else {
                if (maxThreads < 0) {
                    curThreadsBusy++;
                    return (newWorkerThread());
                } else {
                    return (null);
                }
            }
        }
    }

到此,線程已經獲取了,接下來,最關鍵的是調用線程實現Workerrun方法:socket

public void run() {
            // Process requests until we receive a shutdown signal
            while (running) {
                // Wait for the next socket to be assigned
                Socket socket = await();
                if (socket == null)
                    continue;
                   if (!setSocketOptions(socket) || !handler.process(socket)) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                    }
                }
                socket = null;
                recycleWorkerThread(this);
            }
        }

這裏跟請求處理密切相關的是handler.process(socket)這一句代碼,此處handle對應的類是Http11Protocol中的內部類Http11ConnectionHandler,在此後的處理中,會有一些請求的預處理,咱們用一個時序圖來表示一下:post

     在這個過程當中,會對原始的socket進行一些處理,到CoyoteAdapter時,接受的參數已是org.apache.coyote.Requestorg.apache.coyote.Response了,可是要注意的是,此時這兩個類並非咱們經常使用的HttpServletRequestHttpServletResponse的實現類,而是Tomcat內部的數據結構,存儲了和輸入、輸出相關的信息。值得注意的是,CoyoteAdapterservice方法中,會調用名爲postParseRequest的方法,在這個方法中,會解析請求,調用MapperMap方法來肯定該請求該由哪一個EngineHostContext來處理。this

    在以上的信息處理完畢後,在CoyoteAdapterservice方法中,會調用這樣一個方法:spa

connector.getContainer().getPipeline().getFirst().invoke(request, response);

    這個方法會涉及到Tomcat組件中的Container實現類的重要組成結構,即每一個容器類組件都有一個pipeline屬性,這個屬性控制請求的處理過程,在pipeline上能夠添加Valve,進而能夠控制請求的處理流程。能夠用下面的圖來表示,請求是如何流動的:線程

能夠將請求想象成水的流動,請求須要在各個組件之間流動,中間通過若干的水管和水閥,等全部的水閥走完,請求也就處理完了,而每一個組件都會有一個默認的水閥(以Standard做爲類的前綴)來進行請求的處理,若是業務須要的話,能夠自定義Valve,將其安裝到容器中。code

後面的處理過程就比較相似了,會按照解析出來的EngineHostContext的順序來進行處理。這裏用了兩張算不上標準的時序圖來描述這一過程:

在以上的流程中,會一層層地調用各個容器組件的Valveinvoke方法,其中StandardWrapperValve這個標準閥門將會調用StandardWrapperallocate方法來獲取真正要執行的Servlet(在Tomcat中全部的請求最終都會映射到一個Servlet,靜態資源和JSP也是如此),並按照請求的地址來構建過濾器鏈,按照順序執行各個過濾器並最終調用目標Servletservice方法,來完成業務的真正處理。

以上的處理過程當中,涉及到不少重要的代碼,後續的文章會擇期要者進行解析,如:

Mapper中的internalMapWrapper方法(用來匹配對應的Servlet)

 ApplicationFilterFactory的createFilterChain方法(用來建立該請求的過濾器鏈)

ApplicationFilterChain的internalDoFilter方法(用來執行過濾器方法以及最後的Servlet)

 Http11Processor中的process方法、prepareRequest方法以及prepareResponse方法(用來處理HTTP請求相關的協議、參數等信息)


至此,咱們簡單瞭解一個請求的處理流程。

相關文章
相關標籤/搜索