簡介
本文繼續(xù)以tomcat8 為例,簡單分析下Tomcat的幾種protocol的差異忌穿,以及處理鏈接的細(xì)節(jié)。
主要有以下內(nèi)容:</br>
- tomcat protocol的分類</br>
- tomcat protocol的實現(xiàn)</br>
- 各個protocol的差異</br>
Tomcat protocol 配置
參考官方文檔,tomcat protocol配置,可以看到protocol
主要是有四種碧注,默認(rèn)使用的HTTP/1.1 ,對于tomcat8 以更高版本來說竖哩,HTTP/1.1
的配置會默認(rèn)使用nio來處理,也就是org.apache.coyote.http11.Http11NioProtocol
怀跛。
其他的幾種:</br>
org.apache.coyote.http11.Http11Protocol
java的bio connector距贷,使用ServerSocket
處理請求。</br>
org.apache.coyote.http11.Http11NioProtocol
java的nio connector吻谋,使用SocketChannel
處理請求.</br>
org.apache.coyote.http11.Http11Nio2Protocol
java7新出的aio connector忠蝗,使用AsynchronousSocketChannel
處理請求。</br>
org.apache.coyote.http11.Http11Nio2Protocol
tomcat的native library connector漓拾。(這個我們稍后再講)</br>
對于tomcat8 以更高版本來說阁最,HTTP/1.1的配置會默認(rèn)使用nio來處理,也就是
org.apache.coyote.http11.Http11NioProtocol
骇两。由org.apache.catalina.connector.Connector
的setProtocol()
方法也可以看到:
public void setProtocol(String protocol) {
//如果配置了apr速种,會默認(rèn)使用apr(apr后面再講)
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
//這里可以看到,HTTP/1.1是server.xml的默認(rèn)配置低千,會默認(rèn)使用nio處理
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11NioProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpNioProtocol");
} else if (protocol != null) {
//其他情況下配阵,使用指定的protocol
setProtocolHandlerClassName(protocol);
}
}
}
Tomcat protocol的處理流程
在Connector
的startInternal()
方法會啟動protocol
@Override
protected void startInternal() throws LifecycleException {
// Validate settings before starting
...
try {
protocolHandler.start();
} catch (Exception e) {
...
}
}
這里的protocolHandler,就是上面setProtocol()方法指定的protocol示血。暫時以Http11NioProtocol
為例棋傍,分析下請求處理流程。上面startInternal()
方法接下來會到org.apache.coyote.AbstractProtocol
的start()
方法难审。
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
//每個protocol都有一個對應(yīng)的enpoint舍沙,最終是由endpoint來負(fù)責(zé)處理鏈接的。
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
每個protocol對應(yīng)了一個Endpoint</br>
Http11Protocol
對應(yīng)org.apache.tomcat.util.net.JIoEndpoint
</br>
Http11NioProtocol
對應(yīng)org.apache.tomcat.util.net.NioEndpoint
</br>
Http11Nio2Protocol
對應(yīng)org.apache.tomcat.util.net.Nio2Endpoint
</br>
繼續(xù)查看Nio2Endpoint
的startInternal()
方法.
@Override
public void startInternal() throws Exception {
if (!running) {
...
//初始化最大連接數(shù)限制剔宪,在server.xml中可配置
initializeConnectionLatch();
// Start poller threads
// poller 主要負(fù)責(zé)檢查各個 Selector 的狀態(tài)以及處理超時等
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
//啟動接受鏈接的線程
startAcceptorThreads();
}
}
startAcceptorThreads()
方法會啟動Endpoint
內(nèi)部的Acceptor
拂铡。繼續(xù)查看org.apache.tomcat.util.net.NioEndpoint.Acceptor
的run()
方法:
@Override
public void run() {
...
// Loop until we receive a shutdown command
while (running) {
...
try {
//檢查下當(dāng)前是否已經(jīng)達(dá)到了最大鏈接數(shù)
//if we have reached max connections, wait
countUpOrAwaitConnection();
SocketChannel socket = null;
try {
// Accept the next incoming connection from the server socket
socket = serverSock.accept();
} catch (IOException ioe) {
...
}
// Successful accept, reset the error delay
errorDelay = 0;
// setSocketOptions() will add channel to the poller if successful
// setSocketOptions 會把channel添加到poller
if (running && !paused) {
if (!setSocketOptions(socket)) {
countDownConnection();
closeSocket(socket);
}
} else {
countDownConnection();
closeSocket(socket);
}
} ...
}
state = AcceptorState.ENDED;
}
}
從代碼中可以看到壹无,使用 SocketChannel 接受連接,然后通過 setSocketOptions 方法把 channel 交給 poller 處理感帅。setSocketOptions(SocketChannel socket)
方法則是真正處理鏈接的地方斗锭。
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
//disable blocking, APR style, we are gonna be polling it
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
NioChannel channel = nioChannels.pop();
...
//會把 調(diào)用poller的 register 方法,把 channel 交給 poller
getPoller0().register(channel);
} catch (Throwable t) {
...
}
return true;
}
getPoller0() 會根據(jù)設(shè)置的poll線程數(shù)失球,返回一個當(dāng)前可用的 Poller 對象岖是,使用Round robin算法實現(xiàn)一個簡單的負(fù)載均衡。
public Poller getPoller0() {
int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
return pollers[idx];
}
接下來查看 register()
方法
public void register(final NioChannel socket) {
socket.setPoller(this);
// 新建一個 KeyAttachment 對象实苞,保存跟socket相關(guān)的一些信息
KeyAttachment ka = new KeyAttachment(socket);
ka.setPoller(this);
ka.setTimeout(getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
PollerEvent r = eventCache.pop();
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER);
//比較重要的是這句豺撑,把 r 添加到當(dāng)前的 events 隊列中。
addEvent(r);
}
上面可以看到黔牵,每個請求進(jìn)來之后聪轿,會通過 register 方法創(chuàng)建一個 PollerEvent 對象并添加到當(dāng)前的 SynchronizedQueue<PollerEvent> events 隊列中。
接下來看 Poller的run()
方法猾浦,上面已經(jīng)說過陆错,Poller 在startInternal()
方法調(diào)用的時候創(chuàng)建并啟動。
@Override
public void run() {
// Loop until destroy() is called
while (true) {
try {
...
boolean hasEvents = false;
// Time to terminate?
if (close) {
events();
timeout(0, false);
...
break;
} else {
//調(diào)用 events() 方法
hasEvents = events();
}
}
...
}//while
stopLatch.countDown();
}
接下來查看events()方法
public boolean events() {
boolean result = false;
PollerEvent pe = null;
while ( (pe = events.poll()) != null ) {
result = true;
try {
//調(diào)用 PollerEvent 的 run() 方法
pe.run();
pe.reset();
if (running && !paused) {
eventCache.push(pe);
}
} catch ( Throwable x ) {
log.error("",x);
}
}
return result;
}
可以看到金赦,在tomcat啟動之后音瓷,會啟動相應(yīng)的endpoint的Acceptor來接受請求,同時啟動相應(yīng)的Poller來處理請求夹抗。</br>
PollerEvent的run方法
@Override
public void run() {
//對于新增的鏈接绳慎,會注冊給 poller 的 selector 處理。
if ( interestOps == OP_REGISTER ) {
try {
//把當(dāng)前的key注冊給poller中的selector對象漠烧,準(zhǔn)備后續(xù)處理杏愤。
socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, key);
} catch (Exception x) {
log.error("", x);
}
} else {
...
}//end if
}//run
再回過來看 Poller 的 run() 方法。里面有一段
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
KeyAttachment attachment = (KeyAttachment)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (attachment == null) {
iterator.remove();
} else {
attachment.access();
iterator.remove();
processKey(sk, attachment);
}
}//while
會檢查selector
所有的key沽甥,然后處理調(diào)用processKey()
進(jìn)行處理。
processKey()
會根據(jù)配置的Executer
調(diào)用SocketProcessor
的 run()
方法.
接下來看SocketProcessor
的run()
方法最終會調(diào)用doRun()
方法乏奥,在doRun()
里有如下代碼:
if (status == null) {
state = handler.process(ka, SocketStatus.OPEN_READ);
} else {
state = handler.process(ka, status);
}
接下來會調(diào)用到handler
的process
方法摆舟,這個handler就是在Http11NioProtocol
的構(gòu)造方法里由setHandler
設(shè)置的handler,也就是Http11ConnectionHandler
繼承了 AbstractConnectionHandler
,接著來到 org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
的 process
方法邓了,這里會對每個socket做處理恨诱。包括調(diào)用具體的servlet處理業(yè)務(wù)等等。
總結(jié)
tomcat在啟動的時候骗炉,根據(jù)配置的protocol照宝,啟動不同的Endpoint中的Acceptor 和 Poller。Acceptor負(fù)責(zé)接受請求句葵,Poller負(fù)責(zé)調(diào)用線程池執(zhí)行業(yè)務(wù)厕鹃。