前言:這是幾個月前壓測某項目登錄接口時遇到的性能問題,雖然大家不一定會遇到,但是分析定位問題的思路還是可以參考一下。
1、壓測過程中,tps突然劇烈下降,且所有請求失敗(下圖綠線)
服務端錯誤日誌,獲取不到redis連接池(Could not get a resource from the pool),另外,從下圖可以看到,當前jedis版本是2.9.1
獲取不到連接,可能是這四種情況:
Timeout waiting for idle object
Pool exhausted
Unable to activate object
Unable to validate object
下圖,表示等待空閒連接超時
2、jedis的連接池就是用commons-pool2來管理的,使用jvisualvm打開對應的應用進程,根據上圖的提示,找到org.apache.commons.pool2
可以看到相關的配置
活動連接1000,連接滿了
dump堆內存進行分析(此時壓測已經停止)
根據文章開頭的日誌信息,搜索org.apache.commons.pool2.impl,然後雙擊下面的DefaultPooledObject類
進入實例
實例狀態是ALLOCATED
ALLOCATED表示在使用中,壓測結束後,雖然連接釋放了,但是資源沒歸還
下面可以看到,dataSource爲空
如果dataSource爲空,就走else,說明只關閉了連接,資源沒歸還到隊列中,後面的線程就獲取不到空閒連接
可以看到,實例有很多
爲什麼會出現這種情況呢?
通過查看源碼及官網,可以確定,這是2.9.1的bug,改爲2.9.0就沒報錯了。
官網:https://github.com/xetorthio/jedis/pull/1918/commits/df1bffa3c77f4ede4c912f2c3e78b5c8857725e7
Move dataSource reset before connection returned to pool.
If datasource reset after the connection returned to pool, it might reset the datasource after it was allocated to another thread
意思是:在連接返回到池之前重置移動數據源。
如果數據源在連接返回到池之後重置,那麼它可能會在將數據源分配給另一個線程之後重置該數據源
3、出現很多實例狀態是ALLOCATED,dataSource爲null的原因:
在多線程時,如果dataSource在連接釋放後重置(根據代碼邏輯可知:連接釋放前,資源已經歸還,但是未重置),可能在重置前,這個dataSource已經分配給另外一個線程了,此時重置,就把已經獲取了這個dataSource的線程的dataSource重置了,這樣就導致很多狀態是ALLOCATED、dataSource值爲null的實例,進而這些線程都只關閉了連接,而沒有歸還資源,最終導致獲取不到連接,即文章開頭的異常日誌信息,這也印證了都是壓測一段時間後纔開始報錯的現象。
另外,有些性能問題,還是需要一定的代碼能力,而且,測試會代碼是個趨勢。