当服务器达到文件描述符限制时,你的系统往往会立刻出现问题。你可能会注意到 CPU 使用率升高、连接失败,或者出现一些难以直接定位原因的异常现象。这些症状会打乱你的工作流程,也会影响用户体验。及时处理有助于你恢复服务器的正常运行,并防止数据丢失。

  • CPU 使用率高
  • 连接失败
  • 难以追踪的问题症状

只要采取正确的步骤,你就可以解决这些问题,并让服务器持续平稳运行。

文件描述符限制问题的紧急处理步骤

常见症状和错误信息

当服务器触及文件描述符限制时,你可能会发现系统行为异常。应用程序可能停止响应,用户可能会遇到连接失败。有时,你还会在日志中看到一些指向资源问题的错误信息。以下是最常见的一些错误:

错误信息说明
ERROR: lib_htresponse: htresponseGetContentBlock: Failed to allocate the content block处理内容时发生内存分配失败。
ERROR: lib_htresponse: htresponseGetChunk: Failed to allocate the chunk分块处理时发生内存分配失败。
ERROR: lib_htrequest: htrequestWrite: Failed to allocate memory通用内存分配失败。
ERROR: as_handler: failed to create pool创建资源池时因超出资源限制而失败。
[alert] (11)Resource temporarily unavailable: apr_thread_create: unable to create worker thread由于资源限制,无法创建工作线程。
[alert] (12)Cannot allocate memory: apr_thread_create: unable to create worker thread创建工作线程时发生内存分配失败。
[error] server reached MaxClients setting, consider raising the MaxClients setting表示服务器已达到最大客户端连接数限制。

你应当检查日志中是否出现这些信息。它们能够帮助你确认:打开的文件描述符数量已经达到了系统范围内的限制。

快速释放文件描述符

面对这种问题时,你需要尽快采取行动。首先,找出哪些进程占用了最多的打开文件描述符。你可以使用下面的命令列出所有打开的文件:

lsof | less

如果你想查看哪个进程占用最多,可以尝试:

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

这条命令会显示文件描述符计数最高的进程 ID。如果你发现某个进程本不应占用如此多的文件,可以停止它或重启它。你也可以关闭未使用的应用程序或连接。这样可以释放资源,帮助服务器恢复正常。

提示:有时,单个应用程序可能会发生文件描述符泄漏。请检查是否存在失控增长的日志文件,或卡住的网络连接。

重启服务或进程

如果释放资源后问题仍未解决,你可能需要重启相关服务。重启服务会释放它当前持有的全部文件描述符。可以使用以下命令重启服务(将 service_name 替换为实际服务名):

sudo systemctl restart service_name

如果你不知道是哪一个服务引发了问题,可以先重启最常见的关键服务,例如 Web 服务器或数据库服务器。在某些情况下,你可能还需要重启整台服务器。不过,这应当作为最后手段。

你也可以通过以下命令查看当前 shell 会话允许打开的最大文件描述符数量:

ulimit -n

如果你需要临时提高限制,可以使用:

ulimit -n 4096

这条命令会为当前会话设置新的限制。请记住,这项更改不会影响其他用户,也不会改变系统范围的限制。

按照这些步骤操作后,你可以快速恢复正常运行。同时,在进一步排查根本原因的过程中,也能避免问题继续扩大。

检查已打开的文件描述符数量

使用 lsof 和其他工具

你需要知道服务器当前使用了多少打开的文件描述符。lsof 命令可以清晰展示所有打开的文件,并帮助你迅速定位问题。你可以使用以下命令进行检查和监控:

  • lsof:列出所有打开的文件,包括套接字和管道。
  • sudo lsof | head -20:显示前 20 个打开的文件。使用 sudo 可获取完整结果。
  • sudo lsof | awk 'NR>1 {print $1, $2}' | sort | uniq -c | sort -rn | head -20:统计每个进程的打开文件描述符数量并按降序排序。
  • lsof -p $(pgrep myapp) | wc -l:统计指定进程的文件描述符数量。
  • watch -n 2 "lsof -p $(pgrep myapp) | wc -l":每两秒监控一次某进程的文件描述符数量。

lsof 工具能帮助你找出哪些进程消耗了最多资源。你也可以借此发现被锁定或未释放的文件,这些都可能是问题根源。如果你发现某个进程的计数异常偏高,那么你很可能已经找到了问题所在。

使用 ulimit 和系统文件查看限制

你还应当检查当前会话的文件描述符限制。ulimit -n 命令会显示允许打开的最大文件描述符数量。如果你想调整当前会话的限制,可以使用 ulimit -n <number>。如果需要更精细的控制,可以使用 ulimit -Sn <number> 设置软限制,或使用 ulimit -Hn <number> 设置硬限制。

如果你希望永久生效,就需要编辑系统文件。请更新 /etc/security/limits.conf 来设置用户级限制,或更新 /etc/systemd/system.conf 来设置服务级限制。在修改系统服务限制后,请运行 systemctl daemon-reload,然后重启对应服务。

下表展示了常见 Linux 系统上的默认限制:

发行版 / 系统软限制硬限制
Home Assistant OS1024524288
Linux 系统1024N/A
systemd 240 或更高版本1024524288

如果你运行的是大型应用或多个服务,就应当检查并修改系统限制。这一步能够帮助你避免再次触及文件描述符上限。

提高文件描述符限制

当你达到文件描述符限制时,需要迅速处理,以保持服务器稳定。提高限制主要有两种方式:一种是临时提高当前会话的限制,另一种是永久提高整个系统的限制。你应根据自身需求以及所运行应用的类型选择合适的方法。

使用 ulimit 进行临时修改

你可以使用 ulimit 命令修改当前 shell 会话允许打开的最大文件描述符数量。这种方式适合快速应急,也适合在永久修改前先做测试。高负载环境,例如繁忙的 Web 服务器或数据库服务器,在流量高峰时通常需要更高的限制。备份工具、文件同步程序以及存在文件描述符泄漏的应用,也都可能从临时提高限制中受益。

常见需要提高限制的场景包括:

  • 拥有大量工作线程或 goroutine 的应用程序
  • 一次性打开大量套接字或文件的程序
  • 处理高流量或大量连接的服务器
  • 未正确轮转或关闭的日志文件

要查看当前限制,请运行:

ulimit -n

要为当前会话设置新的限制,请使用:

ulimit -n 65536

该命令会将当前 shell 的打开文件描述符数量限制设置为 65,536。如果你关闭 shell 或重启服务器,限制会恢复为默认值。若要设置非常高的数值,可能需要超级用户权限。

如果设置得过低,你可能会看到诸如 Nginx 拒绝新连接、MongoDB 无法读写数据之类的错误。其他应用程序也可能因无法打开足够多的文件而停止工作。

在 sysctl.conf 和 limits.conf 中进行永久修改

若要获得长期解决方案,你应当更新系统配置文件。sysctl.conf 文件控制系统范围内的文件句柄限制。参数 fs.file-max 用于设置内核可使用的最大文件句柄数量。对于运行大量应用程序或同时处理大量用户的服务器来说,这个设置尤为重要。

如果你不提高系统范围的限制,可能会看到 “Too many open files” 之类的错误。这些错误可能引发程序崩溃和服务中断。你还应更新 /etc/security/limits.conf 来设置用户级限制。这个文件允许你控制每个用户或用户组能够打开多少文件。

要修改系统范围限制,请在 /etc/sysctl.conf 中添加以下内容:

fs.file-max = 100000

使用以下命令应用更改:

sudo sysctl -p

要设置用户限制,请在 /etc/security/limits.conf 中添加如下内容:

* soft nofile 65536
* hard nofile 65536

星号(*)表示该规则适用于所有用户。如果你愿意,也可以将其替换为某个具体用户名。软限制是 shell 默认使用的值,硬限制则是你可设置的最大值。修改后,你可能需要退出登录并重新登录,才能让这些更改生效。

为 systemd 服务应用更改

许多现代 Linux 系统使用 systemd 来管理服务。要更改这些服务的文件描述符限制,你必须更新服务配置。请按以下步骤操作:

  1. 为你的服务创建一个覆盖配置文件:
    sudo systemctl edit your-service-name.service
  2. [Service] 部分下添加这一行:
    LimitNOFILE=65536
  3. 重新加载 systemd 守护进程:
    sudo systemctl daemon-reload
  4. 重启你的服务:
    sudo systemctl restart your-service-name.service
  5. 检查新的限制是否已经生效。先找到主进程 ID:
    systemctl status your-service-name.service

    然后运行:

    cat /proc/[PID]/limits | grep "Max open files"

你也可以使用 systemd-delta --type=extended 来确认更改是否已生效。命令 systemctl status your-service-name.service 也会显示当前设置。

提示:每次修改后都要重新加载 systemd 守护进程。并且要重启服务,新的设置才会真正应用。

通过这些步骤,你可以控制应用程序能够使用的文件描述符数量。这有助于避免服务故障,并保持服务器平稳运行。

防止文件描述符耗尽

使用 Netdata 或 Zabbix 进行监控

你可以通过使用具备实时数据和告警能力的监控工具,来防止文件描述符耗尽。Netdata 和 Zabbix 都可以帮助你跟踪打开的文件和套接字,而无需手动反复检查。这些工具能让你在问题影响用户之前就及时发现异常。

工具优势
Netdata零配置即可获得即时洞察,并支持按秒级采集数据。
Zabbix监控能力全面,具备强大的历史数据存储和可自定义告警功能。

Netdata 提供实时可见性,因此你可以在变化发生时立即看到。Zabbix 则支持监控大量设备和应用,并能保存数据用于长期分析。两者都能减少你在手动监控上花费的时间和精力。你还可以设置告警,在使用量接近文件描述符限制时提前收到通知。

提示:自动化监控能让你在专注其他工作的同时,依然有信心及早发现问题。

优化日志和应用程序设置

你应当检查应用程序配置,避免不必要的文件描述符占用。很多应用会因为日志记录、网络连接或缓存机制而打开文件或套接字。如果不对这些设置进行优化,就可能遇到 “Too many open files” 之类的错误。

参数说明
文件描述符每个套接字连接都会占用一个文件描述符。合理调优这一数值可提升可用性。
Accept Backlog控制有多少个 TCP 连接可以在队列中等待处理。设置该值有助于管理负载。
Socket Health Check通过调整健康检查超时,只保留健康连接,减少资源浪费。

你应当在读取或写入配置文件后立即关闭文件描述符。尽量将打开的文件描述符数量保持在 50 以下。定期重启应用程序,有助于清理未使用的资源。还要始终检查 ulimit 设置,确保应用程序能够承受预期负载。

定期审查与团队意识建设

你可以把定期审计纳入日常流程,以防止未来再次出现问题。经常检查服务器资源使用情况和配置文件。对服务器进行压力测试,了解其极限并为未来增长做好规划。使用限流机制来控制服务器接受的请求数量。

  • 培训团队掌握资源管理模式。
  • 分享与资源泄漏相关事故的复盘总结。
  • 将资源管理纳入编码规范。
  • 为高风险代码区域制定检查清单。

定期审查和团队培训能帮助每个人在问题变得严重之前,就及时发现并修复它们。

你可以通过以下步骤解决文件描述符限制问题:

  1. 以预期峰值负载的两倍对服务器进行测试。
  2. 监控文件描述符数量和 TCP 状态。
  3. 在服务器初始部署时就设置好 ulimits

对于高并发服务器,请持续关注各项指标,并谨慎调整限制。如果设置过高,可能会带来系统不稳定的风险。

最佳实践命令示例
提高文件描述符数量echo "* soft nofile 1000000" >> /etc/security/limits.conf
提高系统范围限制echo "fs.file-max = 2000000" >> /etc/sysctl.conf

保持主动预防,并不断学习,才能更好地守护服务器的健康运行。

常见问题

什么是文件描述符?

文件描述符是操作系统用来跟踪打开文件、套接字或管道的一个数字标识。每个运行中的进程都有自己的一组文件描述符。

我怎么知道服务器是否达到了文件描述符限制?

你可能会在日志中看到 “Too many open files” 这样的错误。使用 lsofulimit -n 可以检查当前使用情况和限制值。

我可以在不重启服务器的情况下提高文件描述符限制吗?

你可以使用 ulimit -n <number> 提高当前会话的限制。若要永久生效,则必须更新系统文件,并重启受影响的服务。

是什么导致文件描述符泄漏?

应用程序可能没有正确关闭文件或套接字。糟糕的日志处理方式或代码中的缺陷,通常都会导致泄漏。定期监控有助于你及早发现这些问题。