目標:在這篇文章希望搞明白http請求到tomcat後是怎麼由連接器轉交到容器的?
在上一節裡已經啟動瞭一個HttpConnector線程,並且也啟動瞭固定數量的HttpProcessor線程。HttpConnector用來等待http連接,得到http連接後交給其中的一個HttpProcessor線程來處理。接下裡具體看一下HttpConnector是怎麼得到連接得,以及HttpProcessor是怎麼處理的。當啟動瞭HttpConnector線程後(在上一節已經知道怎麼啟動瞭),便在它的run方法裡面循環等待:
[java]
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 = serverSocket.accept();
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]
private Stack processors = new Stack();
表明httpConnector裡面持有一個包含HttpProcessor對象的棧,需要的時候拿出來就是瞭。看一下createProcessor函數就能比較明白瞭:
[java]
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]
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]
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方法主要的代碼如下:www.aiwalls.com
[java]
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);
………………………………………
}
作者:haitao111313