一次MongoDB的Socket Exception
今天重新部署了一個專案,該專案啟動的時候會訪問MongoDB獲取一些資料,一個蠻簡單的專案,從前釋出都沒問題,這次啟動的時候直接就是Socekt Exception:
nested exception is org.springframework.data.mongodb.UncategorizedMongoDbException: socket exception [SEND_ERROR] for x.x.x.x:xx; nested exception is com.mongodb.MongoException: socket exception [SEND_ERROR] for x.x.x.x:xx
第一反應是內網網路不太好,網路波動嘛,經查有的事兒。
解決辦法:多試幾次……
很明顯,這個辦法沒解決問題(這要是解決了就不會在這兒寫這個了)。同時ping mong例項所在的主機,0.3ms之內就返回了,也沒有丟包。
然後去mongo那邊看了看,也沒啥問題,執行的很正常,log裡也沒什麼錯誤。 而且Mongo這邊配置的最大連線數是2W,這連線現在才用了不到9000啊。
這事情就比較奇怪了,以前從來沒遇到,既然是Socekt Exception只能繼續忘網路方向想了。
首先檢視一下tcp連線中各個狀態的連線數:
admin@linux:~> netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 3615
CLOSE_WAIT 130
FIN_WAIT1 11
FIN_WAIT2 69
ESTABLISHED 1983
SYN_RECV 7
CLOSING 23
LAST_ACK 39
LISTEN 30
這個命令呢,我也是搜來的,上面顯示的是一臺服務執行正常的機器上面的結果,而出問題那臺呢,ESTABLISHED有6000多,這明顯不科學呀,就業務層面來看,它的壓力應該更小點才對。
這得看一下這多出來的連線是和哪臺機器建立的,繼續:
netstat -an |grep ESTA |awk '{print$5 "\n"}' |awk 'BEGIN {FS=":"} {COUNT [$1]++}END{for(a in COUNT) print a, COUNT[a]}' |sort -k 2 -nr
打印出來所有和該機器建立連線的IP以及連線數,一看果不其然是跟mongo所在的機器建立的連線超多,佔到了ESTABLISHED連線的絕大部分。
這得看一下是哪個程序出的問題,繼續:
netstat -anp |grep ESTA |awk '{print$7 }' |awk ' {COUNT[$1]++}END{for(a in COUNT) print a, COUNT[a]}' |sort -k 2 -nr
這樣看一下到底哪些程序佔用的ESTABLISHED連線多,拿到程序號之後,很容易找到對應的服務,結果呢另外一個服務佔用了很多ESTABLISHED連線。
netstat -anp | grep {pid}| grep ESTA |awk '{print$5 "\n"}' |awk 'BEGIN {FS=":"} {COUNT[$1]++}END{for(a in COUNT) print a, COUNT[a] }'
跟據pid反過來可以檢視此程序和哪些機器建立了連線,以及建立了多少連線。
這個服務居然佔用了5000個mongo連線,太誇張了!!大概就是它把連線佔完了,這就是罪魁禍首了!
等等,我們的mongo不是最大有2w個連線嗎?就算這個出奇的佔了這麼多也不至於讓別的起不來呀!先把5000這個錯誤修正讓服務跑起來再說,檢查了一下,這是配置上的問題,connections-per-host
這個引數配置成了500,改成一個合理的值比如100,所有都恢復正常了,原先的服務也能起來了。
接下來分析兩個問題:
connections-per-host
這個引數是什麼意思- mongo的最大連線數怎麼沒生效
先說第二個,這個比較簡單。
主要原因在於,即使mongo的配置裡面最大連線數是2w,系統裡面每個程序的最大開啟檔案數(open files)只有65535,所以mongo的最大連線數其實也只有65535。連線數有問題的那個服務佔掉了5000個,剩下的被其他服務佔用了,所以重新部署的那個服務無法再獲得新的連線了。
這個地方唯一比較讓人鬱悶的是mongo這邊log裡也沒報錯……
再說第一個問題。官方API中對這個引數的解釋是這樣的:The maximum number of connections allowed per host for this Mongo instance.
。允許每個host對這個Mongo例項建立的最大連線數。 根據使用的情況,個人反倒覺得解釋為允許每個process對這個Mongo例項建立的最大連線數更合適一些。如果是允許每個機器建立的最大連線數,那麼同一臺機器上不管部署多少個服務連線到這個Mongo上面,這個機器和Mongo之間的最大連線數都應該是設定的這個值,可實際情況並不是這樣。
另外,也沒發現Mongo連線池有回收的機制,依據個人使用經驗,連線數目基本上只增不減(即使在不需要這麼連線的時候),最多增至設定的connections-per-host
。
上面這些分析是個人的一個見解,可能不準確,歡迎指正。