《深入剖析Tomcat》讀書筆記(一)

mingziday發表於2016-01-23

一、Tomcat

Tomcat,全名Apache Tomcat,最初是由Sun發起,後來捐贈給ASF,是Apache Jakarta下的一個子專案。
Tomcat是對Servlet API定義的容器的一個完整實現,同時它也不僅僅是一個容器,也完全可以當做一個純Java實現的HTTP伺服器來使用,Tomcat自帶的Servlet容器的名稱為Catalina。
Tomcat 的心臟是兩個元件:Connector 和 Container,一個 Container 可以選擇對應多個 Connector。多個 Connector(多個Connector的原因應該是多種網路協議吧) 和一個 Container 就形成了一個 Service,有了 Service 就可以對外提供服務了,但是 Service 還要一個生存的環境,該環境由Server提供,所以整個 Tomcat 的生命週期由 Server 控制。

即:

Server:控制Tomcat的start/stop,通過server的start/stop,能夠一路暢通地把下面所有的service/connector/container一起start/stop

Service:配置多個Connector的目的一般是為了適應不同的http協議,即不同的協議由不同的connector處理,但都關聯到一個Container

Container:Container由四個級別,有高層到底層分別為Engine、Host、Context、Wrapper,具體介紹在下一篇部落格解釋

二、Tomcat聯結器Connector的啟動

Tomcat不推薦使用預設聯結器,而是使用Coyote(郊狼),但預設的連結器依然是一個比較好的學習範本。本小結的分析就是基於預設連機器。
Connector 元件的主要任務是負責接收瀏覽器的發過來的 tcp 連線請求,建立一個 Request 和 Response 物件分別用於和請求端交換資料,然後會產生一個執行緒來處理這個請求,並把 Request 和 Response 物件傳給處理這個請求的執行緒。

Connector聯結器處理請求的時序圖:


下面是Connector啟動的入口:

public final class Bootstrap {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
connector.initialize();//對應上圖第一步,建立全域性的ServerSocket
connector.start();
}
}

 HttpConnector的start方法如下:

public void start() throws LifecycleException {
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our background thread
threadStart();
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
//建立多個processor,並且每個processor都呼叫了本執行緒的start方法進行啟動
//將process壓入堆疊,該堆疊用來儲存多個processor
HttpProcessor processor = newProcessor();
recycle(processor);
}
}

 threadStart()會啟動後臺執行緒,於是進入該connector的run()方法,上圖中的第3步

private void threadStart() {
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
}
public void run() { while (!stopped) { // Accept the next incoming connection from the server socket Socket socket = null; try { socket = serverSocket.accept();//connector會阻塞在accept方法,等待http連線 socket.setTcpNoDelay(tcpNoDelay); } // Hand this socket off to an appropriate processor // 連線帶來後獲取processor,獲取方式很簡單,就是從stack中彈出一個 HttpProcessor processor = createProcessor(); // 呼叫processor的assign方法,assing會通知processor執行緒去處理具體的動作 // 本處呼叫直接返回,提高了效率 processor.assign(socket); // The processor will recycle itself when it finishes }//結束while synchronized (threadSync) {   threadSync.notifyAll(); }
}//結束run

上述為Connector執行緒的啟動過程,下面就要講Processor執行緒的處理過程了,注意二者是不同的處理執行緒。
上面講到processor執行緒進行了啟動,即每個執行緒都執行了run方法,該run方法如下:

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
process(socket);

// Finish up this request
connector.recycle(this);
}

// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}

 await方法會阻塞住,直到被通知,這是上圖中的第5步驟,這個通知的發出是由connector呼叫processor的assign方法發出的。await()和assing的實現如下:

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);

}
Procesor的assign方法如下:
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();
}

 整個過程描述總結如下:

1、processor執行緒啟動的時候,avaliable=false,執行緒會在await方法中等待,直到其他執行緒呼叫notify()或者notifyAll()方法
2、客戶端發起請求,新的socket通過assing()方法傳入processor,此時avaliable依然為false,assign方法會跳過while,設定available=true,併發出通知,告訴processor執行緒可以向下繼續執行
3、processor執行緒收到通知,此時avaliable=true,await方法會跳過while,執行process方法,進行處理。
4、process方法中,將處理好request和response, connector.getContainer().invoke(request, response);交給container去處理。問題是,到底交個container中的哪個servlet去處理呢?後續文章介紹吧

參考資料

《Tomcat深度剖析》

https://www.ibm.com/developerworks/cn/java/j-lo-tomcat1/

 

相關文章