jMeter測試單個介面測試都沒有問題,多個介面依次測試,就會有部分The target server failed to respond,有的會返回Connection reset。我們一步步來看看怎麼解決
找到造成failed to respond的原因
先看一下服務端的相關配置:
預設的tcp_keepalive_time是7200;
tomcat的server.xml的配置 connectionTimeout是20s
從抓包檢視瀏覽器中一個正常訪問的開始
----
客戶端ip:192.168.1.101,服務端ip: 192.168.1.50
訪問 /api/vi/home
----
從下圖wireshark的抓包資料,可以看到22行,客戶端回了一個ACK,這次請求的資料已經傳出完畢了,服務端連線的狀態是 ESTABLISHED。
----
20s後,服務端發了一個FIN+ACK,主動關閉這個連線(因為tomcat中設定了20s超時),四次揮手這個連線就關閉了。
如果 /api/vi/home 這個訪問響應完後的20s內,有新的請求產生,就會複用這個tcp連線,就不用在3次握手了。
----
從下圖可以看到
第一次請求/api/vi/home 經過了三次握手
緊接著請求/api/v1/home/seckill?sec=2 就直接傳資料了
這是複用了tcp連線。
如果超過了20s,連線被服務端主動關閉了,瀏覽器中再次請求的時候,就會建立一條新的連線。
----
這也就是HttpClient的連線池機制,可以複用tcp連線,這樣能有效減少服務端開銷和縮短響應時間。
----
在瀏覽器中訪問,服務端主動去關閉的時候,客戶端也就跟著關閉了,四次揮手!
但在jMeter中,如果20s超時,服務端發來FIN+ACK,客戶端回一個ACK,但客戶端並不會再回一個FIN+ACK,也就是說不理會服務端的主動關閉
----
這時服務端的tcp連線的狀態是FIN_WAIT2, 客戶端的tcp連線的狀態是CLOSE_WAIT。
這也就是為什麼服務端有很多FIN_WAIT2的原因!
到下一個介面測試的時候,複用了這個連線,服務端tcp就會返回RST,而http就是空應答,HttpClient在解析HTTP頭時,發現沒有資料,因為沒有返回HTTP資料包,而是返回了TCP資料包。
----
就會在jMeter中看到如下錯誤:
Response code: Non HTTP response code: org.apache.http.NoHttpResponseException
Response message: Non HTTP response message: The target server failed to respond
復現空閒超時被服務端主動關閉的情況
這是jMeter中ideltimeout的配置:
----
bin/jmeter.properties 這個檔案中,有關於ideltimeout的配置,目前是註釋的。
我們測試執行緒數是2,兩個介面(seckill介面,home介面),迴圈次數1
seckill介面的地址:
/api/v1/home/seckill?sec=${sec}
----
home介面的地址:
/api/v1/home
----
在seckill介面中
/api/v1/home/seckill?sec=1的時候 sleep了30s
/api/v1/home/seckill?sec=2的時候,正常處理
這樣就能保證,/api/v1/home/seckill?sec=2的這條連線等到測試home介面的時候,已經超過了20s,被服務端主動關閉了
----
看抓包結果:
(ideltimeout註釋了下的情況)
可以看到: 埠57513(/api/v1/home/seckill?sec=2),在空閒20後(51行)的時候,被服務端主動關閉,等到下一個介面/api/v1/home要用的時候,服務端返回RST(63行)
----
這是jMeter中返回的結果:
我們開啟ideltimeout,看看這個timeout好不好使
ideltimeout == 10s時:
----
可以看到20s後(53行)被服務端主動關閉,/api/v1/home/seckill這個介面併發完成後(69行是都完成了),才在(70行)主動去關閉埠為57590的這個超時連線。
這都超了33秒了,早就超過ideltimeout==10s的設定了
----
但這個設定還是還是管用的,不然也不會有(70行)的主動關閉連線。
我們再看看ideltimeout == 20s的時候:
----
可以看到無論是20s 還是10s,連線都不是實時釋放的,都是等這個介面測試完,才去釋放的,也就是說,這個ideltimeout 是等一個介面測試完,然後再去檢視每個連線的空閒時間,如果有的超過了ideltimeout,就主動關閉它。
那麼我們就知道:
jMeter有兩種主動關閉連線的方式
----
1:一個介面併發完,空閒時間超過 ideltimeout 的執行緒,幹掉
2:全部測試完成,主動關閉所有連線。
介面測試總結
通過以上可總結三種情況錯誤的情況如下:
(以2000併發為例)
----
1:ideltimeout不設定,只設置connectionTimeout==20s
----
一批介面中
第一個介面肯定不會出現,因為他都是新建的tcp連線。
----
第二個介面容易出現RST,因為第一個介面新建連線比較耗時,容易造成測到第二個介面的時候,一些連線已經超過了20s,被服務端主動關閉了。
----
耗時的介面上,容易出現RST
比如介面4,複用介面3的2000連線(我們以1~2000標記),說是併發執行,其實在服務端也是個佇列,假設到複用1900以後的了連線了,1900以前已經耗時了20s, 這1900以後的連線其實已經被服務端主動關閉了。(有些連線在介面3中已經空閒了一段時間,到介面4都不用20s就被關閉了 )
----
耗時的介面的下一個介面,容易出現RST,原因耗時介面到下一個介面的時候,有些空閒的時間比較長
----
所以如果不設定ideltimeout,很容易造成累計效應,越到最後,可能出錯的機率越高
2:ideltimeout < connectionTimeout的情況
----
設定 ideltimeout == 10s,設定connectionTimeout==20s
----
第一個介面肯定不會出現,同上
----
第二個接口出錯的機率比第一種情況就小很多,因為到第二個介面的時候沒有連線 的空閒時間是超過了10s的,只有連線複用的時候如果空閒超過了20s,被服務端主動關閉的連線才會出錯。
----
耗時的介面上,容易出現RST,但比第一種情況機率小一些。
----
耗時的介面的下一個介面,比第一種情況機率也小一些。
3:當Ideltimeout > connectionTimeout時:
----
設定 ideltimeout == 60s,設定connectionTimeout==20s
----
他比第一種情況好,比第二種情況差。
所以出錯的機率大概是這樣的:(由大到小)
1:ideltimeout不設定,只設置connectionTimeout
2:Ideltimeout > connectionTimeout
3:ideltimeout <= connectionTimeout
解決辦法
正常的客戶端訪問,不會出現這樣的情況,因為正常通訊的情況下,服務端主動關閉,客戶端連線也會釋放掉。
----
但是jMeter中,服務端主動關閉,他根本就不理會。那麼有沒有jMeter中實時主動關閉的辦法那?
有
----
去掉勾選Use KeepAlive
連線就不會複用,這樣也就不存在空閒超時,被服務端幹掉的情況了。
但帶來的是效能的開銷,因為tcp每次都要關閉然後重建。
可是,正常瀏覽器訪問請求頭中也是帶Connection:keep-alive的,所以去掉的話,測試出的吞吐量就不太合理。
那這個問題就落在服務端的主動關閉上了,不讓服務端主動關閉就行了。
----
把 connectionTimeout 儘可能的調大,
建議 connectionTimeout = 最耗時介面的一次併發的總時間 * 介面個數
Ideltimeout <= 最耗時介面的一次併發的總時間
----
測試完,把connectionTimeout這個值在改回原值即可。