How Tomcat works — 五、tomcat啟動(4),tomcatworks
前面擺了三節的姿勢,現在終於要看到最終tomcat監聽端口,接收請求了。
目錄
- Connector
- Http11Protocol
- JIoEndpoint
- 總結
在前面的初始化都完成之後,進行Connector的初始化,也是執行一些生命周期方法。
Connector
在啟動過程中這個類的主要作用是初始化並啟動CoyoteAdapter和Http11Protocol:
initInternal
- 新建一個CoyoteAdapter,並調用protocolHandler.setAdapter方法設置adapter(最後protocol創建processor處理完成之後會調用adapter.service方法)
- 初始化protocolHandler(在Connector構造函數中就獲得了該實例),實際調用的是超類的init方法
- 初始化mapperListener
startInternal
作用很簡單了
- 啟動protocolHandler
- 啟動mapperListener
Http11Protocol
前面在第一節中說到Connector的時候繪制了tomcat中關於協議的類圖,對於不同的協議有不同的連接器,這裡主要說關於http11的,ajp也類似。這個類的主要作用就是啟動一個JIoEndpoint)(其內部類Acceptor是最終啟動線程接收請求的類)。
特別需要關注的是該構造函數:
- new JIoEndpoint()
- new Http11ConnectionHandler(),這個類是Http11Protocol的內部類,負責獲取processor來處理請求
- ((JIoEndpoint)endpoint).setHandler設置endpoint中handler
init
雖然是Http11Protocol的實例,但是執行的是父類的init方法,主要的操作在父類的父類的init方法中AbstractProtocol.init
- 注冊MBean
- 調用JIoEndpoint.init(該類在)初始化JIoEndpoint
start
- endpoint.start()啟動JIoEndpoint
JIoEndpoint
來到了tomcat啟動的最後一站,要啟動線程監聽端口接收請求了。繼承關系
所以也用到了很多父類的方法,這個的作用就是新建socket,綁定到對應端口
綁定到端口之後,就可以調用socket.accept()接受請求了
所以startInternal方法主要進行了一下操作
- 如果Executor(用來執行processor線程)為null則新建
- 啟動Acceptor線程,接受請求就在這個線程裡面
- 啟動一個請求超時檢測線程JIoEndpoint$AsyncTimeout
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
state = AcceptorState.PAUSED;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
countUpOrAwaitConnection();
Socket socket = null;
try {
// Accept the next incoming connection from the server
// socket
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
countDownConnection();
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused && setSocketOptions(socket)) {
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} else {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} catch (IOException x) {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), x);
}
} catch (NullPointerException npe) {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), npe);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
state = AcceptorState.ENDED;
}
}
View Code
終於看到夢寐以求的socket.accept了!雖然只是為了最後這一步,tomcat廢了這麼多周折,作用還是很重要的:
- 用來管理生命周期的Lifecycle,給這個容器的生命周期提供了規范和基本實現
- 便於擴展的容器架構,多Host可用來分布式,多Context部署多個webapp,Wrapper用來作為servlet的直接容器(比如如果是SingleThreadModel的話需要有多個servlet實例)
- 可以處理多種協議的Connector
總結
到這裡tomcat 這個啟動過程算是完成了,當然了裡面還是有很多細節略過了,不過還是很值得仔細進行專題學習,比如:server.xml的解析,web.xml的解析(這樣也能明白平時配置的都有什麼作用),tomcat的熱加載功能是怎麼實現的(對的,就在啟動過程中啟動了一個線程)等等。這些都做了筆記,以後再整理下。