注:為了單純的瞭解聯結器執行過程,與別的元件相關部分程式碼被註釋了,該篇文章只是簡單的對重點程式碼進行解釋,理論知識可以參考《how tomcat works》這本書,感覺還是不錯的。
1.啟動(這是自己寫的一個簡單啟動程式碼)
private static void start() {
HttpConnector connector = new HttpConnector();
connector.start();
}
2.HttpConnecter類中的start()方法
public void start() {
// Validate and update our current state
// if (started)
// throw new
// LifecycleException(sm.getString("httpConnector.alreadyStarted"));
threadName = "HttpConnector[" + port + "]";
// lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
//啟動當前執行緒
threadStart();
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
//將當前執行緒建立處理器加入處理器池中
recycle(processor);
}
}
3.HttpConnecter類中threadStart()方法
private void threadStart() {
// log(sm.getString("httpConnector.starting"));
// 啟動當前的connector的執行緒
thread = new Thread(this, threadName);
// 設定為守護執行緒
thread.setDaemon(true);
thread.start();
}
4.HttpConnector類的run()方法
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();
// 設定連結超時(假設設定為10分鐘)以後,如果客戶端socket和服務端socket連結以後10中以後會自動斷開
// tcp連結,預設是一支不設定超時就是一直連結的
if (connectionTimeout > 0)
socket.setSoTimeout(connectionTimeout);
// 決定是否使用nagle演算法,也就是將小資料包先不傳送,附加在下一個資料包後面傳送過來
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();
// if (debug >= 3)
// log("run: Reopening server socket");
// 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
// 啟動完一個proccessor執行緒以後,先讓它們處於wait狀態,因為此時並沒有需要處理的請求。
HttpProcessor processor = createProcessor();
if (processor == null) {
try {
// log(sm.getString("httpConnector.noProcessor"));
socket.close();
} catch (IOException e) {
;
}
continue;
}
// 將tcp連線封裝的socket物件分配給Processor處理
processor.assign(socket);
// The processor will recycle itself when it finishes
synchronized (threadSync) {
threadSync.notifyAll();
}
}
}
5.HttpConnector類的assign()方法
synchronized void assign(Socket socket) {
// Wait for the Processor to get the previous Socket,預設為false
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
//通知processor的執行緒現在可以啟動了
available = true;
//通知自己的在等待的執行緒可以啟用了,他們會自己去競爭資源
//因為是用當前processor的物件來進行wait的(),因此就是通知當前processor的執行緒執行,而不是所有
//在等待processor執行緒
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
接下來看看與處理器建立相關程式碼,因為一個聯結器中有可以有多個處理器,因此檢視各個處理器建立的過程是很有必要的,這裡的多個處理器採用的是處理器池是實現的,跟dbcp資料庫連線池的實現方式很相似,接下來看程式碼
6.httpConnector中createProcessor()方法
注,其中processors的宣告是這樣的:
private Stack<Object> processors = new Stack<Object>();
private HttpProcessor createProcessor() {
synchronized (processors) {
// 池中有處理器可用
if (processors.size() > 0) {
return ((HttpProcessor) processors.pop());
}
// 如果處理器池中沒有可以用的處理器,則判斷當前的處理器個數(此時池中是空的)是否大於設定最大處理器個數,
if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
// 沒有則建立
return (newProcessor());
} else {
// 如果處理器最大個數被設定為-1,則要多少個處理器就建立多少個,不受限制
if (maxProcessors < 0) {
return (newProcessor());
} else {
return (null);
}
}
}
}
7.httpConnector中newProcessor()方法
private HttpProcessor newProcessor() {
// 當建立一個處理器,則當前處理器標記加1,給改處理器設定了個名字,執行緒也是這麼做的
HttpProcessor processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
// 在HttpProcessor類中的start方法裡啟動processor執行緒(沒建立一個處理器就馬上啟動,但是啟動萬以後並沒有讓processor的run方法馬上執行,而是出於wait狀態)
((Lifecycle) processor).start();
} catch (Exception e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor);
}
接下來我們看看HttpProcessor中相關程式碼
1.HttpProcessor中start()方法
// 基本上都是用在start方法中啟動自己的執行緒的。
public void start() // throws LifecycleException
{
// if (started)
// throw new LifecycleException
// (sm.getString("httpProcessor.alreadyStarted"));
// lifecycle.fireLifecycleEvent(START_EVENT, null);
// started = true;
threadStart();
}
2.HttpProcessor中start()方法
private void threadStart() {
//log(sm.getString("httpProcessor.starting"));
thread = new Thread(this, threadName);
//設定為守護執行緒,因為tomcat是為web應用程式服務的,所以他的執行緒一般都是守護執行緒
//同時,在守護執行緒中建立的執行緒即使不setDaemon,也是守護執行緒
thread.setDaemon(true);
//開啟processor中的run方法。
thread.start();
if (debug >= 1)
log(" Background thread has been started");
}
3.HttpProcessor中run()方法
public void run() {
// Process requests until we receive a shutdown signal
while (!stopped) {
//當前執行緒等待下一個socket來以後啟用所有的等等的執行緒(就是處理器建立以後不馬上執行完,而是出於wait狀態等待socket來以後並被別的執行緒啟用)
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
try {
//該方法就是處理socket,使用者解析http頭,建立request和response物件(以後會單獨詳細講)
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();
}
}
4.HttpProcessor中await()方法
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while (!available) {
try {
//就是呼叫當前物件的wait()方法
wait();
} catch (InterruptedException e) {
}
}
}
5.HttpProcessor中process()方法
public void process(Socket socket)
{
//該方法是處理http頭和內容的,接下來會詳細講
}
到此,tomcat中聯結器的執行過程基本結束了,怎麼處理socket會在之後詳細講