今天重新部署了一个项目,该项目启动的时候会访问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的配置里面最大连接数是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
。
上面这些分析是个人的一个见解,可能不准确,欢迎指正。