該文描述瞭解決問題的過程
大概描述的是PooledConnectionFactory連接池 連接大面積釋放時造成系統擁堵的解決方案
如僅關心解決方案請拖到最下方即可
涉及:
JAVA + Spring + ActiveMQ + PooledConnectionFactory
前情提要:
隨着業務的增長 在一個月黑風高的晚上 由於MQ獲取不到連接 導致平臺無法正常運行 於是乎修改了最大連接數(maxConnections=10000)
發現問題:
一直以爲MQ的問題得以解決,直到發現平臺頻率性的異常,最終把目光又投向了MQ。
通過日誌打印連接數,發現每分鐘獲取近150連接,盯着它盯了1個小時多一點,發現連接數到達之前設置的maxConnections=10000,這個時候開始發現連接以200多個的速度開始釋放,此時平臺頻率性的異常出現了,直到連接重新到達0時MQ才重新恢復工作。
該釋放連接的過程一條異常日誌都沒有。這纔是最氣的!!!
搜索解決方案:
不知道是我關鍵字沒輸入對還是咋的,查了一波都是教如何使用PooledConnectionFactory亦或者是改用spring的。Emm 我不想改線上代碼,我覺得改了代碼又得整天盯着它有沒有問題。
那竟然它到了maxConnections就開始釋放,那第一時間想到肯定是因爲之前連接沒釋放所以導致最後一次性釋放導致平臺阻塞近50秒。
搜到的就幾個參數 IdleTimeout 、 ExpiryTimeout 。
idleTimeout:空閒的連接過期時間,默認爲30秒 即 連接開始空閒的時間戳 + idleTimeOut < 當前時間 即認爲失效
expiryTimeout : 連接過期時間,默認爲0 和上面不一樣的是連接被創建開始 + expiryTimeout < 當前時間 即認爲失效 0應該是不失效吧,沒細看
於是乎就針對這兩個參數,填了兩個數字上去(別問我填了啥,因爲最終是沒效果,所以我就不說了)
最終結果就是又盯了1個多小時 開始大面積回收連接 就是這個結果並沒有發生改變,連接只增不減,到達最大值再給你一次性減了。
看源碼:
synchronized是阻塞的關鍵
如果獲取連接數小於最大連接數(getConnectionsPool().getNumIdle(key) < getMaxConnections())則正常獲取連接,否則循環獲取可用連接。
挺正常的代碼,重點就在borrowObject 在裏面代碼循環連接,如果判斷連接失效則調用destroy
此處問題就是,你設置的失效參數是當所有連接拿了一輪之後(即1萬個連接都用了後)才判斷前面的連接是否失效。那當然失效了呀,1萬個連接一輪共花1個多小時,而失效時間是30秒,所以在到達1萬連接後開始大面積釋放(destroy),並且synchronized讓後面的連接只能等待釋放完成後再獲取。
再次查找解決方案:
仔細品了這篇文章後https://blog.csdn.net/u013760858/article/details/49664449問題得以解決。別問我爲啥之前沒搜索到,因爲這文章文字密密麻麻的,哈哈 但是最後看完之後受益匪淺。
大概意思講的是
裏面維護了一個LinkedList,每次獲取鏈接都從list取出ActivemqConnection給你使用並放在list的最後,通過這個方式做簡單的負載均衡。
所以當獲取鏈接一輪之後其實是可以再次使用之前的ActivemqConnection,而且ActivemqConnection是可以被多條線程同時使用的,只是創建session的是一個線程只會有一個。
那這就很明確了,之前的想法是希望線程提前釋放,那現在應該是想辦法不要讓他釋放。
解決方案:
通過上面的分析,得出的辦法就是儘可能讓連接在失效前進入下一輪使用,就是想辦法不要讓他釋放。
當正常使用mq時算的應該是session數而不是connection(連接)數, 默認值下 session數是500,即1條連接可以保證500條線程工作。
如果業務量大 且服務器支持的前提下則將maxConnections設置大,並且將idleTimeout失效時間設置長一點,他兩的關係就是在idleTimeOut時間內業務必須將maxConnection都創建完並使用一輪
例如: 平臺每分鐘150條連接 那maxConnection<150, idleTimeOut=60000
maximumActiveSessionPerConnection,該參數設置一個連接最多支持創建多少session,可以設的相對小一些,它的默認值是500,這值太大,擔心內存溢出。
具體設置還是看你的業務需求
寫在結尾:
以上並沒有查閱大量文獻,僅針對近幾個小時觀察得出的猜測,如有哪塊理解錯誤請聯繫我修正。歡迎交流~