同事新上线了一个日志 sdk。运行一段时间后导致线上服务器打开太多文件而拒绝服务。
原因很简单每一个日志实例化,都持有了一个写文件的流,写完没有关闭。
随着业务运行导致服务器崩溃。
fd (file descriptor) 是什么?
我们先来介绍一下 fd,一个进程所有打开的文件可以通过 fd 查询。
举例来说,当一个程序(进程)要写文件时,会像操作系统申请权限。操作系统会授予一个标记(通常是数字)来指向所描述的文件。这个标记就是 fd。
在 centos 中,一个进程的所有打开的 fd 在 /proc/进程id/fd
下。
如何排查
比如一个 node 服务, 我们先找一下他的进程 id
1 | ps aux | grep node |
第二列就是进程 id。有了进程 id 就可以查一下具体的 fd
1 | sudo ls -l /proc/29027/fd |
当打开文件数量过多时,可以通过命令查看打开连接的总数:
1 | sudo ls -l /proc/29027/fd | wc -l |
结论
从现象定位问题是比较简单的。上面写了一些排查还有测试过程。这里在描述一下解决方案:
在 nodejs 中,流是一个非常重要的概念。在日志这个场景中。我们打开一个流,日志直接往内部写就可以了。在进程退出或者日志路径切换过程中销毁并新建即可。而不需要每次都新建一个流。
所以用全局的流来写日志是一个不错的方案。即便是多进程,以及按等级分不同的流,它的复杂度也是 O(n)。高效并且可控。
1 | 类似 appendFile 这种 buffer 形式写文件,每次写入都需要打开关闭,不适合来做日志。 |
参考
ISSUE
有问题?来 github 一起讨论。