當http鏈接池趕上keepalive

轉載: http://mp.weixin.qq.com/s?__biz=MzAwNzY4OTgyNA==&mid=2651824719&idx=1&sn=c854f9fb5d6a85e9dbac177e08f95ca9&scene=1&srcid=0827jY7OYr9z2q8SgfGyJUAi#rdjava

當http鏈接池趕上keepalive

2016-07-08 Zline DevOps DevOps
DevOps

Idevops168python

運維自動化開發:python、django、saltstack、tornado、bootstrap、redis等經驗分享!web

最近在使用netty做爲http客戶端經過pool鏈接tomcat的時候,出現了不少Connection reset by peer 的IOException的異常。便對問題的根源作了細緻的調研。redis

1 鏈接種類

通常鏈接主要分爲長鏈接,短鏈接和http的keepalive鏈接。數據庫

1.1 長鏈接:創建完鏈接後,該鏈接再也不進行釋放。django

優勢:性能較高,不須要重複創建tcp鏈接或者關閉tcp鏈接bootstrap

基本上不會出現CLOSE_WAIT和TIME_WAIT的問題tomcat

缺點 : 通常須要一個鏈接池來維護長鏈接(通常有數據庫鏈接池,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)

相關文章
相關標籤/搜索