# 4. 停止与重启 HAProxy

> 信号、软停止、重载和主从重启

---

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

---

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

HAProxy 支持优雅停止和强制停止。强制停止操作简单：当向 HAProxy 进程发送 SIGTERM 信号时，进程立即退出，所有已建立的连接将被关闭。优雅停止通过向 HAProxy 进程发送 SIGUSR1 信号触发，其操作仅包括解除对监听端口的绑定，但会继续处理现有连接，直至所有连接关闭。当最后一个连接关闭后，进程退出。

硬停止方法用于服务管理脚本的“stop”或“restart”动作。
优雅停止用于“reload”动作，该动作尝试在新进程中无缝重载新配置。

在重载或重启过程中，新的 HAProxy 进程自身可发送这两个信号，以确保信号在最晚可能时刻发出，且仅在绝对必要时才发送。这分别由“-st”（强制）和“-sf”（优雅）选项实现。

在主进程/工作进程模式下，无需启动新的 HAProxy 进程即可重载配置。主进程收到 SIGUSR2 信号后，会以 -sf 参数及工作进程的 PID 列表重新执行自身。随后，主进程将解析配置文件并创建新的工作进程。

为更好地理解这些信号的使用方式，有必要了解完整的重启机制。

首先，一个现有的 HAProxy 进程正在运行。管理员使用特定于系统的命令，例如 "/etc/init.d/haproxy reload"，以表明希望使新的配置文件生效。随后发生以下过程：首先，服务脚本（/etc/init.d/haproxy 或等效脚本）将使用 "HAProxy -c" 验证配置文件是否能正确解析。之后，将尝试使用该配置文件启动 HAProxy，命令为 "-st" 或 "-sf"。

然后 HAProxy 会尝试绑定所有监听端口。如果发生严重错误（例如：地址在系统中不存在、权限被拒绝），进程将报错退出。如果因端口已被占用而导致套接字绑定失败，则进程会先向“-st”或“-sf”中指定的所有 PID 发送 SIGTTOU 信号。此信号被称为“暂停”信号，它指示所有现有的 HAProxy 进程暂时停止监听其端口，以便新进程重新尝试绑定。在此期间，旧进程仍继续处理现有连接。如果绑定仍然失败（例如端口被其他守护进程共享），则新进程会向旧进程发送 SIGTTIN 信号，指示其恢复操作，如同什么都没发生过一样。旧进程随后将重新开始监听端口并继续接受连接。请注意，该机制依赖于系统，某些操作系统在多进程模式下可能不支持此功能。

如果新进程成功绑定到所有端口，则向所有进程发送 SIGTERM（在 "-st" 情况下为强制停止）或 SIGUSR1（在 "-sf" 情况下为优雅停止），以通知它们新进程现已接管操作，旧进程须立即退出，或在完成当前任务后退出。

请注意，在此时间段内，存在两个持续时间仅为几毫秒的小窗口，在高负载情况下可能观察到少量连接失败。通常情况下，每秒新增 10000 个连接时，重载操作期间的失败率约为 1 次。这意味着，每秒新增 30000 个连接的高负载站点在每次重载时可能约有 3 次连接失败。这种情况发生在以下两种情形中：

- 若新进程因旧进程的存在而无法绑定，它必须首先经历 SIGTTOU + SIGTTIN 序列，该过程通常持续约一毫秒，涉及数十个前端，期间部分端口将未被旧进程绑定，也尚未被新进程绑定。HAProxy 在支持 SO_REUSEPORT 套接字选项的系统上可规避此问题，因为这允许新进程直接绑定，无需先请求旧进程解除绑定。大多数 BSD 系统几乎自始便支持此功能。Linux 从 2.0 版本开始支持，但在 2.2 版本左右被移除，不过当时已有相关补丁流传。该功能在内核 3.9 中重新引入，因此若观察到连接失败率高于上述情况，应确保内核版本为 3.9 或更高，或相关补丁已回迁至所用内核（可能性较低）。

- 当旧进程关闭监听端口时，内核可能无法始终将仍处于套接字接收队列中的待处理连接重新分配。在高负载情况下，可能在套接字关闭前瞬间收到一个 SYN 数据包，导致向客户端发送 RST 数据包。在某些对丢包零容忍的关键环境中，有时会通过防火墙规则在重载期间阻止 SYN 数据包，强制客户端重传。此方法完全依赖系统特性，因为某些系统可能能够访问其他监听队列，从而避免发送 RST。第二种情况涉及客户端在本地套接字处于 SYN_RECV 状态时刚发送的 ACK。在 HAProxy 进程尚未感知到该连接关闭前，该 ACK 将导致发送 RST 数据包。这种情况更难消除，尽管上述防火墙过滤规则若在重启进程前约一秒钟应用，仍可有效应对。

对绝大多数用户而言，此类丢包根本不会发生，因为他们所承受的负载不足以触发竞态条件。对于大多数高流量用户，只要其系统中至少正确支持 SO_REUSEPORT，故障率仍处于可接受的噪声范围内。
