servlet解析演進(2-1)

上文說了簡單的servlet解析過程。實際上,tomcat在解析servlet的 時候是吧鏈接器定位爲知足如下三個條件:css

一、必須事先接口org.apache.catalina.Connectorjava

2. 必須建立請求對象,該請求對象的類必須實現接口org.apache.catalina.Request。 node

3. 必須建立響應對象,該響應對象的類必須實現接口org.apache.catalina.Response。web

tomcat的鏈接器有不少種類:Coyote、mode_jk、mod_jk2和mode_webapp等。apache

來自瀏覽器的請求類型是有區別的http/0.9 http/1.0 http/1.1.瀏覽器

http/1.1又叫作持久鏈接,上文中一次請求處理完畢就關閉了socket。然而,在現實的頁面請求中須要加載多種資源(js、img css)。若是每次請求都開啓關閉鏈接那將是很是耗時的事情。http/1.1可以讓瀏覽器和服務器保持一種持久化的鏈接,快速的相應請求。這樣,請求的形式就和以前不一樣,他是一塊編碼的形式,並使用特殊頭部transfer-encoding感知發送過來的數據信息。同時,若是瀏覽器有較大數據請求以前發送頭部信息Expect: 100-continue頭部到服務器,等待服務器的相應。若是服務器響應數據可以接受大數據請求,才真正的發送數據,避免先發送大數據而遭服務器拒絕而浪費了傳輸數據的帶寬資源。tomcat

本文重點說明鏈接器的演進。安全


鏈接器的演進主要包括瞭如下幾個方面:服務器

一、處理請求再也不只是一次處理一次請求,而是生成Processor池,用於處理多個鏈接請求。同事Processor處理器也再也不是每次請求新生成實例處理請求,而是從池子中獲取到一個處理器去完成請求。同時,實現了Processor處理方法異步化,讓鏈接器線程可以繼續接受其餘的請求。併發

二、處理器線程和鏈接器線程經過wait() notifyAll()的方式進行線程間通訊。

三、Processor處理器中,增長了標識符,KeepAlive、stopped和http11,爲處理器在處理的過程當中設定「路口」,及時相應請求的變化。

四、socket緩衝區大小再也不是像前文同樣設置爲定值,而是經過獲取鏈接器傳遞過來的參數設置緩衝區大小。

五、處理器的職能更加的豐富。

鏈接處理  parseConnection,針對不一樣的鏈接協議作不一樣的處理。


解析請求 parseRequest


解析頭部 parseHeaders 


六、去掉了靜態處理器,暫時沒法接受靜態資源請求。

------------------------

一、鏈接器啓動:

HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
try {
//初始化鏈接器,打開socket鏈接
connector.initialize();
//開啓監聽,開啓鏈接器線程、將處理器線程啓動併入隊列。
connector.start();
  System.in.read();
}
catch (Exception e) {
  e.printStackTrace();
}

1.1 初始化鏈接、打開socket鏈接(使用工廠模式)

public void initialize()
throws LifecycleException {
//初始化標誌,不能重複初始化
if (initialized)
throw new LifecycleException (
sm.getString("httpConnector.alreadyInitialized"));
this.initialized=true;
    Exception eRethrow = null;
// Establish a server socket on the specified port
try {
//打開socket鏈接
serverSocket = open();
    } catch (IOException ioe) {
        log("httpConnector, io problem: ", ioe);
        eRethrow = ioe;
    } catch (KeyStoreException kse) {
        log("httpConnector, keystore problem: ", kse);
        eRethrow = kse;
    } catch (NoSuchAlgorithmException nsae) {
        log("httpConnector, keystore algorithm problem: ", nsae);
        eRethrow = nsae;
    } catch (CertificateException ce) {
        log("httpConnector, certificate problem: ", ce);
        eRethrow = ce;
    } catch (UnrecoverableKeyException uke) {
        log("httpConnector, unrecoverable key: ", uke);
        eRethrow = uke;
    } catch (KeyManagementException kme) {
        log("httpConnector, key management problem: ", kme);
        eRethrow = kme;
    }
if ( eRethrow != null )
throw new LifecycleException(threadName + ".open", eRethrow);
}
private ServerSocket open()
throws IOException, KeyStoreException, NoSuchAlgorithmException,
       CertificateException, UnrecoverableKeyException,
       KeyManagementException
{
// Acquire the server socket factory for this Connector
    //得到服務器socket工廠實例
ServerSocketFactory factory = getFactory();
// If no address is specified, open a connection on all addresses
  //若是沒有指定地址,在全部地址上打開一個鏈接
if (address == null) {
        log(sm.getString("httpConnector.allAddresses"));
try {
return (factory.createSocket(port, acceptCount));
        } catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
        }
    } 
//在指定地址上打開一個鏈接
try {
        InetAddress is = InetAddress.getByName(address);
        log(sm.getString("httpConnector.anAddress", address));
try {
return (factory.createSocket(port, acceptCount, is));
        } catch (BindException be) {
throw new BindException(be.getMessage() + ":" + address +
":" + port);
        }
    } catch (Exception e) {
        log(sm.getString("httpConnector.noAddress", address));
try {
return (factory.createSocket(port, acceptCount));
        } catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
        }
    }
}

1.二、開啓監聽,開啓鏈接器線程、將處理器線程啓動併入隊列。

//爲鏈接器綁定指定數量的處理器
public void start() throws LifecycleException {    
//判斷該線程的connect開啓狀態,避免重複開啓
if (started)
throw new LifecycleException
            (sm.getString("httpConnector.alreadyStarted"));
threadName = "HttpConnector[" + port + "]";
//打開監聽
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our background thread
    //開啓一個後臺鏈接器線程
threadStart();
//建立處理器,並調用recyle方法將其放置到棧隊列中
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
        HttpProcessor processor = newProcessor();
        recycle(processor);
    }
}

二、鏈接器、容器之間的協做關係


2.1 容器派發socket處處理器線程

public void run() {
// Loop until we receive a shutdown command
    //開啓循環,知道咱們得到告終束命令
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
            //得到socket請求
socket = serverSocket.accept();
   //  設置超時時間
if (connectionTimeout > 0)
                socket.setSoTimeout(connectionTimeout);
//設置tcp nodelay屬性
socket.setTcpNoDelay(tcpNoDelay);
        } catch (AccessControlException ace) {
//拋出鏈接安全錯誤信息
log("socket accept security exception", ace);
continue;
        } catch (IOException e) {
try {
// If reopening fails, exit
                //若是再次開啓失敗,退出
synchronized (threadSync) {
if (started && !stopped)
                        log("accept error: ", e);
if (!stopped) {                
//  關閉異常鏈接
serverSocket.close();             
                  //   從新開啓異常鏈接 
serverSocket = open();
                    }
                }
//  異常處理,包括不限於祕鑰等錯誤請求
} catch (Exception ioe) {
                log("socket reopen, io problem: ", ioe);
break;
            }
continue;
        }   
        //判斷處理器棧是否有可用處理器,沒有按規則生成,有則獲取棧中處理器
HttpProcessor processor = createProcessor();
//獲取處理器爲空,可能狀況是請求數量過多,超過了承受的最大處理器個數
if (processor == null) {
try {
                log(sm.getString("httpConnector.noProcessor"));
                socket.close();
            } catch (IOException e) {
                ;
            }
continue;
        }
//派發socket處處理器
processor.assign(socket);
// The processor will recycle itself when it finishes
}
//獲取到告終束命令,通知其餘線程
synchronized (threadSync) {
threadSync.notifyAll();
    }
}

2.2處理器接收到派發socket

synchronized void assign(Socket socket) {
// d等待該處理器的其它socket處理執行完畢
while (available) {
try {
            wait();
        } catch (InterruptedException e) {
        }
    }
// Store the newly available Socket and notify our thread
    //將新獲取的socket放置處處理器中,併發送線程通知
this.socket = socket;
available = true;
    notifyAll();
if ((debug >= 1) && (socket != null))
        log(" An incoming request is being assigned");
}

2.3  處理器接收到了socket請求,準備處理

public void run() {
// Process requests until we receive a shutdown signal
while (!stopped) {
// Wait for the next socket to be assigned
        //等待鏈接器的socket請求
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
try {
//處理socket請求
process(socket);
        } catch (Throwable t) {
            log("process.invoke", t);
        }
// Finish up this request
        //處理完畢,將該processor線程歸還給Stack processors 用來處理接下來的socket請求
connector.recycle(this);
    }
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
//該處理器線程退出,通知其餘processor
threadSync.notifyAll();
    }
}

未完待續

相關文章
相關標籤/搜索