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


一、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/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM