当 HTTP 连接池遇上 KeepAlive 时
最近在使用netty作為http客戶端通過pool連接tomcat的時候,出現了很多Connection reset by peer 的IOException的異常。便對問題的根源做了細致的調研。
1. 連接種類
一般連接主要分為長連接,短連接和http的keepalive連接。
1.1 長連接:建立完連接后,該連接不再進行釋放。
-
優點
-
性能較高,不需要重復建立tcp連接或者關閉tcp連接
-
基本上不會出現CLOSE_WAIT和TIME_WAIT的問題
-
-
缺點:一般需要一個連接池來維護長連接(一般有數據庫連接池,http的連接池等) 復雜度較高
1.2 短連接:每次請求均需要tcp三次握手建立連接,業務執行,tcp四次揮手關閉連接。
-
優點:實現簡單。
-
缺點:
-
性能較差。大部分都是tcp層面上的交互(新建和關閉tcp連接)
-
系統會出現大量的tcp的狀態是:TIME_WAIT 如果沒有設置SO_RESUSEADDR ,很容易出現端口被占滿的情況。(在關閉完連接時,tcp狀態是TIME_WAIT,只有等2個MSL后,才會進行close掉)
-
1.3 http的keepalive:用于http協議。在http 1.1中,為了解決長連接提出的。
-
優點:用于維護長連接,提升性能
-
缺點:需要在header中進行控制,需要交互控制,相對復雜。
2 keepalive機制
提到keepalive, 容易對下面三種機制混淆:
-
keepalived
-
tcp 的 keepalive
-
http 的 keepalive
2.1 keepalived
用途:高可用,一般是和lvs一起使用。
具體可參考:http://outofmemory.cn/wiki/keepalived-configuration
2.2 tcp的keepalive
用途:socket連接的保活。在新建socket的時候,可以設置SO_KEEPALIVE 進行打開。
keepalive主要有三個參數:
-
tcp_keepalive_time: 一個連接需要TCP開始發送keepalive探測數據包之前的空閑時間。以秒為單位
-
tcp_keepalive_probes: 發送TCP keepalive探測數據包的最大數量,默認是9.如果發送9個keepalive探測包后對端仍然沒有響應,便發送RST關閉掉連接。
-
tcp_keepalive_intvl: 發送兩個TCP keepalive探測數據包的間隔時間,默認是75秒
2.3 http的 keepalive
用途:http的長連接,在http 1.0中使用的為短連接:每一次請求均需要新建tcp連接,http協議數據的發送接收,關閉tcp連接。 該種機制性能很 低,在http 1.1協議中引入了keepalive機制來保持tcp連接。
在http1.0中,全部是短連接,如果想建立長連接,需要在header里面加上keepalive,這樣web服務器看到這個字段,不會立馬關閉連接。而是將tcp連接維持一段時間。如果需要關閉,則在header中寫 keepalive close,來告訴 客戶端需要關閉該連接。
在http1.1中,默認會實現keepalive,如果使用的是http1.1協議,header是不需要加上keepalive的。
3. tomcat8對keepalive的實現
3.1 http 1.0實現
tomcat8中,如果發送的是http1.0的協議。 tomcat8返回的均是1.1的協議。并且不管請求的header有沒有Connection:keepalive ,均會在返回的header中加上connection:close 。下面是訪問tomcat8的截圖:
-
GET請求是http 1.0,但是返回的是1.1的協議:
?
-
返回的header里面有Connection:close
?
3.2 http 1.1實現
tomcat8主要有兩個參數來控制keepalive的機制。keepAliveTimeout 和maxKeepAliveRequests
-
keepAliveTimeout: 默認和soTimeout 值保持一致,該值為20000ms,也就是在這么長時間內沒有通信,tomcat會關閉掉該連接。設置為-1 則代表不會關閉該連接。
-
maxKeepAliveRequests?:默認為100,也就是在keepAliveTimeout時間內,如果使用次數超過100,則會關閉掉該連接。設置為-1,則代表不會關閉連接。在關閉后,會在返回的header上面加上Connection:close 。
如果需要tomcat保持長連接:可配置 maxKeepAliveRequests = "-1" keepAliveTimeout=-"-1" ,則tomcat8不會關閉掉該連接。
4. 連接池對keepalive的處理
主要需要處理兩個地方:
-
1:maxKeepAliveRequests?連接達到默認的設置次數。則會在header上面加Connection:close。
-
在接收web服務器返回的數據時,需要檢查一下header里面是否有Connection:close,如果close,則需要將該連接從連接池里物理關閉掉。否則容易出現connection reset by peer的異常。
-
-
2:keepAliveTimeout?超過該時間沒有流量,則會關閉掉連接。
-
tomcat在連接空閑超過該時間后,會主動關閉掉連接。會向客戶端發送FIN命令。
-
如果是IO(同步socket):則在獲取連接的時候需要檢查一下該socket的連接狀態。 因為tcp在底層已經關閉了該連接。 如果不檢查的話,則會SocketCloseException的錯誤。
如果是NIO(異步channel) :則在selector的時候,read數據的時候,會返回-1,然后將該連接從連接池給物理關閉掉。
5. Connection reset by peer異常
異常場景:
-
1: 當我們往一個對端已經close的通道寫數據的時候,對方的tcp會收到這個報文,并且反饋一個reset報文,當收到reset報文的時候,繼續做select讀數據的時候就會拋出Connect reset by peer的異常。該異常為jdk拋出的異常。在native代碼里面拋出。
-
2:嘗試和未開放的服務器端口建立tcp連接時,服務器tcp將會直接向客戶端發送reset報文
-
3:ack報文丟失,并且超出一定的重傳次數或時間后,會主動向對端發送reset報文釋放該TCP連接
連接池出現該異常分析:
-
1:由于客戶端在收到Connection:close的header時候并沒有物理關閉該連接,而是將該連接返回到了連接池中。
-
2:下一個請求拿到該連接發送數據,由于tomcat的該socket通道已經關閉,tomcat接收到該連接時,便會回復一個RST。
-
3:客戶端在讀取數據(RST的時候,內部會調用(JDK)SocketChannel.read的時候拋出 java.io.IOException(Connection reset by peer)
總結
以上是生活随笔為你收集整理的当 HTTP 连接池遇上 KeepAlive 时的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于 Netty 如何实现高性能的 HT
- 下一篇: MyBatis 与 Hibernate