http無狀態,又是基於tcp,所以每次請求都要握手分手,在頻繁的請求來說,很是浪費,且沒有必要。
於是就有了大家都知道的keep-alive。關於keep-alive的概念,網上一大堆,我就不重複了,盜一張圖,描述一下:
keep-alive主要靠服務器實現,那麼問題來了,作爲java程序的主流服務容器,tomcat 是如何實現keep-alive的呢?
先看tomcat,以主流的nio實現爲例
在NioEndpoint.SocketProcessor#doRun的方法中會處理三次握手:
if (handshake == 0) {
log.info("開啓三次握手驗證");
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
log.info("state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);");
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
log.info("state = getHandler().process(socketWrapper, event);");
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
poller.cancelledKey(key, socketWrapper);
}
} else if (handshake == -1 ) {
getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
poller.cancelledKey(key, socketWrapper);
} else if (handshake == SelectionKey.OP_READ){
socketWrapper.registerReadInterest();
} else if (handshake == SelectionKey.OP_WRITE){
socketWrapper.registerWriteInterest();
}
處理後的finally會將當前的SocketProcessor加入到緩存中:
if (running && !paused && processorCache != null) {
processorCache.push(this);
}
下次Poller在執行processSocket 中會先判斷processorCache是否爲空,不爲空則拿出來直接使用,跳過三次握手
就這麼簡單嗎?
仔細想想,好像不對,tomcat是怎麼判斷兩次請求是應該複用同一個processSocket的呢?除非,第一次創建socketChannel沒有關閉。 那就意味着,這個socketChannel一直阻塞在work threadPool裏?!太可怕,所以tomcat 對keep-alive有很苛刻的要求,線程池容量不夠的情況下也不支持。這篇文章寫完大半年後,看到了H大的文章,不禁眼淚掉下來。我將這篇文章大部分內容刪掉,只保留了自己的思考和線索。以後這類文章少寫,或者放在自己的備忘錄就好了。
H大的文章地址:tomcat對keep-alive的實現邏輯
最後:這裏有一個旨在替換掉tomcat+spring+mybatis的基於netty,支持ioc,router,aop,ddd,restful的極簡後端框架: https://github.com/rongjoker/quarantineJ ,.歡迎star和fork,相信會對你做java開發、併發編程、網絡編程有非常大的幫助.