【发布时间】:2016-08-21 20:35:13
【问题描述】:
我在 tcp 端口上有一个监听套接字。进程本身使用setrlimit(RLIMIT_NOFILE,&...); 来配置进程允许的套接字数量。
对于测试,RLIMIT_NOFILE 设置为 20,当然对于生产,它将设置为一个更大的数字。 20 有利于在测试环境中轻松达到极限。
服务器本身没有描述符泄漏或类似的问题,但试图通过增加 RLIMIT_NOFILE 来解决问题显然是做不到的,因为在现实生活中,无论多高都不能保证不会达到限制设置。
问题是在达到限制后accept 返回Too many open files 并且除非文件或套接字关闭,否则事件循环会立即开始旋转,占用一个内核的 100%。即使客户端关闭连接(例如因为超时),服务器也会循环直到有文件描述符可用于处理并关闭已经死的传入连接。 编辑:另一方面,客户端停顿,没有好方法知道服务器是否超载。
我的问题:在accept 返回Too many open files 之后关闭传入连接是否有一些标准方法来处理这种情况。
我想到了几种肮脏的方法:
- 关闭并重新打开监听套接字,希望所有挂起的连接都将关闭(这很脏,因为在线程服务器中,其他线程可能会获得释放的文件描述符)
- 跟踪打开的文件描述符计数(对于具有一些未跟踪文件描述符的外部库,这无法正确完成)
- 检查文件描述符数量是否接近限制并在情况发生之前开始关闭传入连接(这是特定于实现的,虽然它可以在 Linux 上运行,但不能保证其他操作系统会以相同的方式处理文件描述符方式)
编辑:另一种肮脏和丑陋的方法:
保留一个备用 fd(例如 dup(STDIN_FILENO) 或 open("/dev/null",...)),以防 accept 失败时使用。顺序是:
... accept failed
// stop threads
close(sparefd);
newconnection = accept(...);
close(newconnection);
sparefd = open("/dev/null",...);
// release threads
这种方法的主要缺点是线程同步,以防止其他线程获取刚刚释放的备用 fd。
【问题讨论】:
-
意外保持文件描述符打开的外部库非常罕见。
syslog打开一个到syslogd的套接字,DB 访问库为每个数据库会话都有一个 FD。我希望 10 个 FD 的缓冲区在任何正常的应用程序中都绰绰有余。 -
我确实说过很少见。您大概知道自己在应用程序中使用了哪些类型的库,因此您应该能够估计它们可能会使用哪些 fds,除非您尝试设计的是通用库。
-
可能根本就没有好的解决方案。
-
如果您收到“打开的文件过多”错误,那么不要继续重试?这将解决您通过连续重试获得 100% 的 CPU 使用率这一事实。
-
如果您无法控制外部库对 fd 的猖獗使用,请将其限制在自己的进程中。也许你可以让主服务器进程除了监听连接之外什么都不做,然后将接受的 fd 传递给一组工作进程中的一个,它们可以处理任何行为不良的库。