# 5. 文件描述符限制

> 描述限制、sizing、系统约束及故障排查

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

<!-- Generated by scripts/generate-haproxy-docs.py from pinned upstream text. -->

为确保所有传入连接均能成功处理，HAProxy 在加载时会计算进程生命周期内所需的文件描述符总数。常规 Unix 进程默认被授予 1024 个文件描述符，特权进程可自行提升该限制。这是以 root 身份启动 HAProxy 并由其自行调整限制的原因之一。默认的 1024 个文件描述符大致可支持约 500 个并发连接的处理。该计算基于全局 maxconn 参数，该参数限制每个进程的总连接数，同时考虑监听器数量、启用健康检查的服务器数量、代理检查、对等节点、日志记录器以及可能的其他技术需求。对该数值的粗略估算方法为：将 maxconn 值翻倍，并额外增加数十个，即可得到所需的文件描述符近似数量。

最初，HAProxy 无法自动计算此值，必须通过全局段中的 "ulimit-n" 设置手动指定。这解释了为何至今仍有许多配置中保留该设置。不幸的是，该值常被错误计算，导致在接近 maxconn 限制时出现连接失败，而非在等待所需资源时对新连接进行限流。因此，务必移除任何可能源自极旧版本的残留 "ulimit-n" 设置。

提高文件描述符数量以应对中等负载是必须的，但需进行一些操作系统特定的调整。首先，select() 轮询机制最多只能处理 1024 个文件描述符。实际上，在 Linux 上曾支持更多，但由于某些操作系统附带过于严格的 SELinux 策略，禁止使用 select() 处理超过 1024 个文件描述符，HAProxy 现在在这种情况下拒绝启动，以避免运行时出现任何问题。在所有支持的操作系统上，poll() 均可用，且不受此限制影响。HAProxy 会自动选择该机制，因此无需额外操作即可获得正常配置。但当文件描述符数量增加时，poll() 的性能会显著下降。尽管 HAProxy 尽力降低此性能影响（例如通过内部文件描述符缓存和批量处理），但一个通用的经验是：使用 poll() 处理超过一千个并发连接时，将消耗大量 CPU 资源。

对于基于 2.6 及以上内核的 Linux 系统，将使用 epoll() 系统调用。该机制具有更高的可扩展性，依赖于内核中的回调机制，可保证无论注册监控的文件描述符数量多少，唤醒时间始终恒定。只要检测到该功能，且 HAProxy 已针对某一类 Linux 发行版编译构建，便会自动启用。可通过命令 "HAProxy -vv" 验证其存在与支持情况。

对于支持该功能的 BSD 系统，可使用 kqueue() 作为替代方案。由于其支持批量处理变更，性能远超 poll()，甚至略胜于 epoll()。目前至少 FreeBSD 和 OpenBSD 支持该功能。与 Linux 的 epoll() 类似，其支持情况和可用性会在运行 "HAProxy -vv" 时的输出中报告。

拥有一个优秀的轮询器是一回事，但进程必须能够达到限制才是必须的。HAProxy 启动时，会立即设置新进程的文件描述符限制，并验证设置是否成功。若设置失败，将在进程分叉前报告该问题，以便管理员能够发现。只要进程以 root 身份启动，该设置就应不会失败。然而，若进程由非特权用户启动，则可能失败。如果存在必须不以 root 身份启动 HAProxy 的充分理由（例如：由终端用户或特定应用程序账户启动），则系统管理员可为该特定用户提升文件描述符限制。可通过在用户命令行中执行 "ulimit -n" 验证该设置的有效性。输出结果应反映新的限制值。

请注意：当在用户账户中更改非特权用户的限制时，这些值通常仅在用户登录时被考虑，而在系统启动时运行的某些脚本或 crontab 中则完全不被考虑。这完全取决于操作系统，因此在以这种方式运行 HAProxy 之前，请务必检查 "ulimit -n"。一般建议不要在生产环境中以非特权用户身份启动 HAProxy。另一个重要原因在于，这会阻止 HAProxy 启用某些安全防护功能。

一旦确认系统允许 HAProxy 进程使用请求的文件描述符数量，可能会遇到两个新的系统特定限制。第一个是系统级文件描述符限制，即系统上所有进程打开的文件描述符总数。当达到此限制时，accept() 或 socket() 通常会返回 ENFILE。第二个是每个进程的文件描述符硬限制，它阻止 setrlimit() 设置更高的值。这两项限制均高度依赖于操作系统。在 Linux 上，系统级限制在启动时根据内存总量设定，可通过 "fs.file-max" sysctl 修改。每个进程的默认硬限制为 1048576，但可使用 "fs.nr_open" sysctl 进行更改。

当进程的文件描述符限制设置过低时，可能会在运行过程中观察到文件描述符限制问题。使用 strace 工具时，会报告 accept() 和 socket() 返回 "-1 EMFILE"，表明进程的限制已达到。此时，只需提高 "ulimit-n" 值（或将其移除）即可解决问题。若这些系统调用返回 "-1 ENFILE"，则表示内核的限制已达到，必须调整系统级参数。此类问题必须立即解决，否则会导致高 CPU 使用率（当 accept() 失败时）以及用户可见的连接失败。一种解决方案是降低全局 maxconn 值以强制序列化处理，或禁用 HTTP 持久连接，以促使连接更快释放并重用。
