1. 程式人生 > >一次MongoDB的Socket Exception

一次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,所有都恢復正常了,原先的服務也能起來了。

接下來分析兩個問題:

  1. connections-per-host這個引數是什麼意思
  2. 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

上面這些分析是個人的一個見解,可能不準確,歡迎指正。