线上服务慢性病-句柄泄漏

2,037 阅读4分钟

起源

星期天晚上,我正美滋滋的躺在床上刷小破站,跟徐云流一起浪中国,看着巍峨的昆仑山,正在思考里面有没有住着旧日支配者。。。猝不及防一个电话,喂喂喂,线上服务出问题了,赶紧看一下!!!

唉,脑海中瞬间一万头喜羊羊奔腾而过,虽然在休息日我一向不看消息,但是电话打过来了也没办法,还是看一下吧。

先稳住服务

据描述,公司OP大佬在观察监控时发现线上某node服务进程句柄数一直升高,存在资源泄漏问题,需要安排重启,于是幸运的找到了今晚值班的我(其实我是个菜🐔,人大佬说重启那就重启呗)。

image.png

先把服务稳住再说,大半夜十一二点下床把电脑铺开,新拉个分支上线,一通猛如虎的操作过后,服务重启,句柄数总算是降下来了。开开心心滚去睡觉。

image.png

找找原因

周一的早晨,总是莫名的不想上班,去了公司领导说排查一下原因吧,我说好(bu)的(hui),打开线上的监控看了看,WTF?句柄数还在稳步上升。。

image.png

那就看看具体什么原因吧,领导交代的任务哪有完不成的道理。

首先申请了一台线上机器的权限,看看究竟是哪些进程的句柄在升高,在linux下,我们使用 ulimit -n 指令可以查看到单个进程可以打开的最大文件句柄数,系统默认值1024。

image.png

由于知晓此线上服务是node服务,所以我直接查询了node进程的pid,使用如下命令:

pgrep -l node

一个很简单的命令,pgrep,可以迅速定位包含某个关键字的进程的pid,-l参数,可以显示出pid及进程名字;不加-l只显示pid。

image.png

PS: 使用如下命令可以统计当前进程打开的句柄数,如果存在此问题,可以看到某个进程打开的句柄数一直在上升。

lsof -n| awk '{print $2}'| sort| uniq -c| sort -nr| more

然后使用 ps -aux | grep <pid> 可以看到是哪个进程

执行此命令需要root权限,由于是线上机器,OP给我开不了root权限,以下是在联调机器上运行的效果

image.png

然后根据此pid,查看重启后进程的文件打开数,使用如下命令:

lsof -p <pid> | wc -l

image.png

可以看到打开的文件数,每秒都在稳步上升,已经快逼近极限了,使用lsof -p <pid>就可以看到它究竟打开了些什么文件

image.png

好家伙,一兜子全是log文件,滚了十秒停不下来!!!

至此,终于知道了打开的文件是什么了,那就好整了,去代码里定位原因吧。

在代码库中搜索文件关键词发现,这是一个埋点的方法,用的是第三方的神策SDK

image.png

调了它的track方法之后就出现了这个问题

image.png

复现

基于这是一个已经存在于线上的问题,那么找一个环境直接把master代码布上去应该就可以复现,于是接下来就是申请环境、拉分支、布代码、上机器观察一系列常规操作。

随便进一个有埋点的页面,然后看一下当前node进程打开的文件句柄数

image.png

多点击几次有埋点的地方,然后再看一下

image.png

嗯 确实增多了!!!看一下打开的文件是不是上面的log文件

image.png

是它,成功复现。

解决

那就找找解决办法吧,上github翻了一下sdk的源码,找到一个close方法,应该就是关闭文件句柄的方法,放在代码里试一下

image.png

再点几下埋点看看打开的的文件句柄数

image.png

OK,确实是不增加了,一行代码的事儿,问题解决,封版提测!

写在最后

通过这个问题可以看出,如果句柄一直不关掉,会造成资源泄漏,导致系统性能降低,而且会导致程序出错。千里之堤,溃于蚁穴,广大厂友们写代码时还是要注意啊。不说了,去搬砖了

参考文献

# 如何排查句柄泄露问题

# Node.js 案发现场揭秘 —— 文件句柄泄露导致进程假死

# Linux下查看进程打开的文件句柄数和如何修改