tomcat HTTP處理—— Request的生命歷程和相應Connector配置解析

Request的生命歷程,可以參見常量類org.apache.coyote.Constants.java

    // Request states
    public static final int STAGE_NEW = 0;
    public static final int STAGE_PARSE = 1;
    public static final int STAGE_PREPARE = 2;
    public static final int STAGE_SERVICE = 3;
    public static final int STAGE_ENDINPUT = 4;
    public static final int STAGE_ENDOUTPUT = 5;
    public static final int STAGE_KEEPALIVE = 6;
    public static final int STAGE_ENDED = 7;

 

和用到這些常量的地方 Http11Processor.java (代碼貼的比較多,可以用瀏覽器的 查找功能來查找上面的常量)

 while (started && !error && keepAlive && !endpoint.isPaused()) {

            // Parsing the request header
            try {
                if (keptAlive) {
                    if (keepAliveTimeout > 0) {
                        socket.setSoTimeout(keepAliveTimeout);
                    }
                    else if (soTimeout > 0) {
                        socket.setSoTimeout(soTimeout);
                    }
                }
                inputBuffer.parseRequestLine();
                request.setStartTime(System.currentTimeMillis());
                keptAlive = true;
                if (disableUploadTimeout) {
                    socket.setSoTimeout(soTimeout);
                } else {
                    socket.setSoTimeout(timeout);
                }
                inputBuffer.parseHeaders();
            } catch (IOException e) {
                error = true;
                break;
            } catch (Throwable t) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("http11processor.header.parse"), t);
                }
                // 400 - Bad Request
                response.setStatus(400);
                adapter.log(request, response, 0);
                error = true;
            }

            if (!error) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                    prepareRequest();
                } catch (Throwable t) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("http11processor.request.prepare"), t);
                    }
                    // 400 - Internal Server Error
                    response.setStatus(400);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }

            if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
                keepAlive = false;

            // Process the request in the adapter
            if (!error) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    adapter.service(request, response);
                    // Handle when the response was committed before a serious
                    // error occurred.  Throwing a ServletException should both
                    // set the status to 500 and set the errorException.
                    // If we fail here, then the response is likely already
                    // committed, so we can't try and set headers.
                    if(keepAlive && !error) { // Avoid checking twice.
                        error = response.getErrorException() != null ||
                                statusDropsConnection(response.getStatus());
                    }

                } catch (InterruptedIOException e) {
                    error = true;
                } catch (Throwable t) {
                    log.error(sm.getString("http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    adapter.log(request, response, 0);
                    error = true;
                }
            }

            // Finish the handling of the request
            try {
                rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
                // If we know we are closing the connection, don't drain input.
                // This way uploading a 100GB file doesn't tie up the thread 
                // if the servlet has rejected it.
                if(error)
                    inputBuffer.setSwallowInput(false);
                inputBuffer.endRequest();
            } catch (IOException e) {
                error = true;
            } catch (Throwable t) {
                log.error(sm.getString("http11processor.request.finish"), t);
                // 500 - Internal Server Error
                response.setStatus(500);
                adapter.log(request, response, 0);
                error = true;
            }
            try {
                rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
                outputBuffer.endRequest();
            } catch (IOException e) {
                error = true;
            } catch (Throwable t) {
                log.error(sm.getString("http11processor.response.finish"), t);
                error = true;
            }

            // If there was an error, make sure the request is counted as
            // and error, and update the statistics counter
            if (error) {
                response.setStatus(500);
            }
            request.updateCounters();

            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

            // Don't reset the param - we'll see it as ended. Next request
            // will reset it
            // thrA.setParam(null);
            // Next request
            inputBuffer.nextRequest();
            outputBuffer.nextRequest();

        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

        // Recycle
        inputBuffer.recycle();
        outputBuffer.recycle();
        this.socket = null;
        // Recycle ssl info
        sslSupport = null;
    }

 

 

 

Connector配置解析:

 

官方文檔:

http://tomcat.apache.org/tomcat-6.0-doc/config/http.html

 

文檔中講了比較多的選項的用法,其中,我把比較重要的幾個列舉下:

 

maxKeepAliveRequests

在服務器關閉socket連接之前,能保持的最大請求數。

 

maxThreads

服務器的最大線程數。講白了就是Worker的最大數目,當然如果配置開啓了Executor的話,這個配置項便是沒有用的。

 

這裏你會不會有疑問,線程數和請求數有什麼關係。根據我上一篇文章tomcat分配請求 的分析,一個Worker線程對應一個請求。

那麼你會不會有更多的疑問,maxThreads如何設置得比 maxKeepAliveRequests 小會怎麼辦?

請看下面這段代碼

Http11Processor.java

 int keepAliveLeft = maxKeepAliveRequests;
        int soTimeout = endpoint.getSoTimeout();

        // When using an executor, these values may return non-positive values
        int curThreads = endpoint.getCurrentThreadsBusy();
        int maxThreads = endpoint.getMaxThreads();
        if (curThreads > 0 && maxThreads > 0) {
            // Only auto-disable keep-alive if the current thread usage % can be
            // calculated correctly
            if ((curThreads*100)/maxThreads > 75) {
                keepAliveLeft = 1; // 當前使用線程是最大線程數的75%的時候,會自動禁用keepAlive
            }
        }

 

 while (started && !error && keepAlive) {
……
  if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
                keepAlive = false;
……
}

 

當前使用線程是最大線程的75% 的時候,會自動禁用keepAlive

所以maxThreads設置得比maxKeepAliveRequests 小,則這個maxKeepAliveRequests 設置時沒有效果的。

當maxThreads設置肯定要比 maxKeepAliveRequests 來的大,而且 maxKeepAliveRequests 不會超過maxThreads的75%!!

默認:maxThreads 爲 200

maxKeepAliveRequests  爲100

 

 

keepAliveTimeout

 服務器Socket讀取HTTP請求行到來的限制時間。 以millionseconds爲單位。默認和connectionTimeout 的值一樣。

 

connectionTimeout

單位是毫秒,Connector從接受連接到提交URI請求數據的等待的時間。

開始這個我沒搞懂,不過找到了這篇文章: http://twotwoandapple.blogbus.com/logs/62770043.html  上面的解釋和測試。

也可以看下面的代碼:Http11Processor.java

 while (started && !error && keepAlive) {
            // Parsing the request header
            try {
                if (keptAlive) {
                    if (keepAliveTimeout > 0) {
                        socket.setSoTimeout(keepAliveTimeout);
                    }
                    else if (soTimeout > 0) {
                        socket.setSoTimeout(soTimeout);
                    }
                }
                // 服務器獲取客戶端請求行,這裏會調用inputBuffer的read 方法,引起socket阻塞,阻塞時間超過soTimeout的話就會SocketTimeOutException
                inputBuffer.parseRequestLine();

 

 

disableUploadTimeout

是否允許在上傳操作的時候給與更長的socketTimeOut時間。默認false不允許。

如果設置爲true,則默認爲5分鐘限制,超過5分鐘則會報SocketTimeOut異常。

如果要改這個時間,需要用這個Attribute: timeout 這個官方文檔沒有寫,不知道爲什麼,可能不建議修改吧,因爲5分鐘是和apache 的httpd的時間保持一致的。

類似:

    <Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" disableUploadTimeout="false" timeout="600000"/>

 

  其中HTTP 協議,最好參考下RFC的文檔 結合Http11Protocal 和 Http11Processor來看比較好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章