2.1 基礎(chǔ)知識:從tcp到http,解析tomcat如何保持長連接

在高并發(fā)的項目中經(jīng)常遇到服務(wù)請求無法訪問的情況。本文從tomcat源碼對keep-alive的實現(xiàn)進行探討這個問題。

基礎(chǔ)知識

http請求的keep-alive

http1.1開始支持長連接。請求的頭部會帶上

Connection: Keep-Alive

長連接的作用是減少斷開連接和重新連接的開銷,提高網(wǎng)絡(luò)請求效率。http只是1個協(xié)議規(guī)范,具體的實現(xiàn)請見下文。

java的tcp網(wǎng)絡(luò)通信是通過socket進行。下面是示例代碼。

erverSocket serverSocket  = new ServerSocket(8080, 1,  InetAddress.getByName(“l(fā)ocalhost”));  
Socket socket = null;  
InputStream is = null;  
OutputStream os = null;  
try {  
    socket = serverSocket.accept();//1.監(jiān)聽到客戶端的連接  
    is = socket.getInputStream();  
    os = socket.getOutputStream();  
    Request request = Util.getRequest(is);//2.從輸入流中讀取數(shù)據(jù),并根據(jù)Http協(xié)議轉(zhuǎn)換成請求  
    Response response = Util.service(request);//服務(wù)器內(nèi)部根據(jù)請求信息給出響應(yīng)信息  
    os.writeResponse(response);//3.將響應(yīng)信息寫到輸出流  
} catch (Exception e) {  
    e.printStackTrace();  
} finally {//4.關(guān)閉輸入輸出流及連接  
    if (is != null) {  
        is.close();  
    }  
    if (os != null) {  
        os.close();  
    }  
    socket.close();  
}  

所以可以發(fā)現(xiàn)tcp連接由操作系統(tǒng)底層實現(xiàn),但http由的實現(xiàn)是由java程序?qū)崿F(xiàn)。我們?yōu)g覽器發(fā)送的請求為http,java的后端服務(wù)是典型的有tomcat。name我們可以看一下tomcat是如何實現(xiàn)http的

按圖索驥研究tomcat

linux下tomcat的啟動我們只要運行startup.sh
我們研究下這個文件,其中的內(nèi)容如下,運行startup.sh的實際結(jié)果是運行catalina.sh還帶了參數(shù)start

#略
EXECUTABLE=catalina.sh
#略
exec "$PRGDIR"/"$EXECUTABLE" start "$@"

接著我們看下catalina.sh,可以看到我們運行的java的入口類是org.apache.catalina.startup.Bootstrap,接著我們找Bootstrap源碼的main函數(shù),main函數(shù)啟動項目,開啟端口,處于等待狀態(tài)。main還解析了tomcat的server.xml文件,進行初始化。tomcat的處理http協(xié)議的類是Http11Processor。這個類有個service方法。
方法中有個while循環(huán),還有個keepAlive參數(shù)。其中有一段對keepalive的賦值改變

if (maxKeepAliveRequests == 1) {
    keepAlive = false;
 } else if (maxKeepAliveRequests > 0 && socketWrapper.decrementKeepAlive() <= 0) {
    keepAlive = false;
}

這里的maxKeepAliveRequests 就是server配置文件里的

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
              maxKeepAliveRequests = 10000 />

這個參數(shù)的意思是最多經(jīng)過多少個請求之后將Connection有keep-alive改為close的。
當(dāng)keepAlive設(shè)為false以后,會退出循環(huán)并返回SocketState.CLOSED給調(diào)用者。調(diào)用者收到這個狀態(tài)后會關(guān)閉socket。結(jié)束連接,tcp會進行4次揮手結(jié)束會話。

假設(shè)keepAlive一開始就未設(shè)置,那么就不會進入循環(huán),直接返回調(diào)用者SocketState.CLOSED。
如果keepAlive還沒到最大值,會一直在while的循環(huán)中,持續(xù)處理socket中的內(nèi)容,直到keepAlive失效,或者連接中斷。

了解了原理之后,我們來看看我們的問題。keepAlive開啟會一直占用一個連接,直到socket關(guān)閉。tomcat有最大連接數(shù)參數(shù)是maxConnections,這個值表示最多可以有多少個socket連接到tomcat上。BIO模式下默認(rèn)最大連接數(shù)是它的最大線程數(shù)(缺省是200),NIO模式下默認(rèn)是10000,APR模式則是8192(windows上則是低于或等于maxConnections的1024的倍數(shù))。如果設(shè)置為-1則表示不限制。當(dāng)請求過多時,新的請求不會被接受,老的請求受網(wǎng)絡(luò)io的影響。但老的請求的會話被關(guān)閉的可能性還比較小。所以在搶票之類的程序中,先登入服務(wù)所在的tomcat還是有優(yōu)勢的。從另一個方面來說關(guān)閉keepalive的功能對搶票人來說較公平。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容