目標:在這篇文章但願搞明白http請求到tomcat後是怎麼由鏈接器轉交到容器的?java
在上一節裏已經啓動了一個HttpConnector線程,而且也啓動了固定數量的HttpProcessor線程。HttpConnector用來等待http鏈接,獲得http鏈接後交給其中的一個HttpProcessor線程來處理。接下里具體看一下HttpConnector是怎麼獲得鏈接得,以及HttpProcessor是怎麼處理的。當啓動了HttpConnector線程後(在上一節已經知道怎麼啓動了),便在它的run方法裏面循環等待:tomcat
[java] view plaincopyprint?app
public void run() { 異步
// Loop until we receive a shutdown command socket
while (!stopped) { jsp
// Accept the next incoming connection from the server socket tcp
Socket socket = null; ide
try { 函數
socket = serverSocket.accept(); oop
if (connectionTimeout > 0)
socket.setSoTimeout(connectionTimeout);
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 (IOException ioe) {
log("socket reopen, io problem: ", ioe);
break;
} catch (KeyStoreException kse) {
log("socket reopen, keystore problem: ", kse);
break;
} catch (NoSuchAlgorithmException nsae) {
log("socket reopen, keystore algorithm problem: ", nsae);
break;
} catch (CertificateException ce) {
log("socket reopen, certificate problem: ", ce);
break;
} catch (UnrecoverableKeyException uke) {
log("socket reopen, unrecoverable key: ", uke);
break;
} catch (KeyManagementException kme) {
log("socket reopen, key management problem: ", kme);
break;
}
continue;
}
// Hand this socket off to an appropriate processor
HttpProcessor processor = createProcessor();
if (processor == null) {
try {
log(sm.getString("httpConnector.noProcessor"));
socket.close();
} catch (IOException e) {
;
}
continue;
}
processor.assign(socket);
}
// Notify the threadStop() method that we have shut ourselves down
synchronized (threadSync) {
threadSync.notifyAll();
}
}
這裏很關鍵的就是socket = serverSocket.accept();和processor.assign(socket); 在循環裏面內,serverSocket.accept();負責接收http請求而後賦值給socket,最後交給其中一個processor處理。這裏processor並非等到須要的時候再實例化,而是在HttpConnector初始化的時候已經有了若干個processor,在httpConnector裏有這樣一個聲明:
[java] view plaincopyprint?
private Stack processors = new Stack();
代表httpConnector裏面持有一個包含HttpProcessor對象的棧,須要的時候拿出來就是了。看一下createProcessor函數就能比較明白了:
[java] view plaincopyprint?
private HttpProcessor createProcessor() {
synchronized (processors) {
if (processors.size() > 0) {
return ((HttpProcessor) processors.pop()); //從processors棧中彈出一個processor
}
if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
return (newProcessor());
} else {
if (maxProcessors < 0) {
return (newProcessor());
} else {
return (null);
}
}
}
}
接下來由processor.assign(socket); 記住這個方法是異步的,不須要等待HttpProcessor來處理完成,因此HttpConnector才能不間斷的傳入Http請求,在HttpProcessor裏有兩個方法比較重要,這兩個方法協調處理了由HttpConnector傳來的socket:
[java] view plaincopyprint?
synchronized void assign(Socket socket) {
// Wait for the Processor to get the previous Socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Notify the Connector that we have received this Socket
Socket socket = this.socket;
available = false;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" The incoming request has been awaited");
return (socket);
}
看一下HttpProcessor的run方法:
[java] view plaincopyprint?
public void run() {
// Process requests until we receive a shutdown signal
while (!stopped) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
try {
process(socket);
} catch (Throwable t) {
log("process.invoke", t);
}
// Finish up this request
connector.recycle(this);
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
很明顯,在它的run方法一開始即是調用上面的await方法來等待(由於一開始available變量爲false),因此HttpProcessor會一直阻塞,直到有線程來喚醒它。當從HttpConnector中調用processor.assign(socket),會把socket傳給此HttpProcessor對象,並設置available爲true,調用notifyAll()喚醒該processor線程以處理socket。同時,在await方法中又把available設置成false,所以又回到初始狀態,便可以從新接受socket。
這裏處理socket的方法是process(socket),主要做用有兩點,1:解析這個socket,即解析http請求,包括請求方法,請求協議等,以填充request,response對象(是否是很熟悉,在servlet和jsp開發常常用到的request,response對象就是從這裏來的)。2:傳入request,response對象給和HttpConnector綁定的容器,讓容器來調用invoke方法進行處理。process方法主要的代碼以下:
[java] view plaincopyprint?
private void process(Socket socket) {
input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
//解析一下鏈接的地址,端口什麼的
parseConnection(socket);
//解析請求頭的第一行,即:方法,協議,uri
parseRequest(input, output);
if (!request.getRequest().getProtocol()
.startsWith("HTTP/0"))
parseHeaders(input);//解析http協議的頭部
..............................................
connector.getContainer().invoke(request, response);
.............................................
}
在那些parse××方法裏面會對request,response對象進行初始化,而後調用容器的invoke方法進行處理,至此,http請求過來的鏈接已經完美的轉交給容器處理,容器剩下的問題就是要最終轉交給哪一個servlet或者jsp的問題。前面咱們知道,一個鏈接會跟一個容器相連,一個級別大的容器會有一個或者多個子容器,最小的容器是Wrapper,對應一個servlet,在這裏咱們只要知道請求的路徑決定了最終會選擇哪一個wrapper,wrapper最終會調用servlet的。至少一開始提出來的問題已經明白了。這裏又有一個問題,在調用invoke方法是有這樣的connector.getContainer的代碼,即經過鏈接器獲得跟它關聯的容器,這個鏈接器是何時跟容器關聯上的?詳見下篇:Tomcat源碼分析(三)--鏈接器是如何與容器關聯的?