# 7. CPU 使用率

> 线程、CPU 亲和性、饱和度、profiling 及性能行为

---

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

---

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

HAProxy 通常大部分时间运行在系统空间，仅小部分时间运行在用户空间。经过精细调优的 3.5 GHz CPU 在单核满载 100% 时，每秒可维持约 80000 次端到端连接的建立与关闭。当单核达到饱和时，典型数值为：

- 长 TCP 连接或大 HTTP 对象：系统占用 95%，用户占用 5%
- 短 TCP 连接或关闭模式下的小 HTTP 对象：系统占用 85%，用户占用 15%
- 持久连接模式下的小 HTTP 对象：系统占用 70%，用户占用 30%

规则处理和正则表达式的数量会增加用户空间部分的开销。防火墙规则、连接跟踪以及系统中复杂的路由表则会增加系统空间部分的开销。

在大多数系统上，网络传输期间观察到的 CPU 时间可被分为 4 个部分：

- 中断部分，涉及在目标进程尚未确定之前执行的所有 I/O 接收处理。通常，接收的数据包（Rx packets）会在中断中进行统计。在某些系统（如 Linux）中，中断处理可能被推迟到专用线程执行，此时表现为软中断（softirq），该线程被称为 ksoftirqd/0（对应 CPU 0）。负责处理此负载的 CPU 通常由硬件设置决定，但在软中断情况下，通常可以将处理重新映射到其他 CPU。该中断部分通常被视为寄生性负载，因为它不与任何进程直接关联，但实际上是在为进程准备工作的过程中执行的处理。

- 系统部分，涉及所有通过用户空间调用的内核代码执行的处理。
  例如，系统调用被计入系统时间。所有同步交付的 Tx 数据包均计入系统时间。若因队列已满需延迟处理某些数据包，则这些数据包后续可能在中断上下文中被处理（例如：在收到 ACK 以打开 TCP 窗口时）。

- 用户部分，仅在用户空间运行应用程序代码。HAProxy 仅在此部分运行，尽管其大量使用系统调用。规则处理、正则表达式、压缩和加密均会增加用户空间的 CPU 消耗。

- 空闲部分，即 CPU 在无事可做时的运行状态。例如，HAProxy 会等待传入连接，或等待数据发出，这意味着系统正在等待客户端的 ACK 以推送这些数据。

在实际分析 HAProxy 的活动时，通常可以合理地认为：中断/软中断由内核驱动中的接收（Rx）处理引起，用户空间时间由 HAProxy 中的第 7 层处理引起，系统时间则由发送（Tx）路径上的网络处理引起。

由于 HAProxy 在事件循环中运行，它通过 poll()（或任何替代方法）等待新事件，并在返回 poll() 以等待新事件之前尽可能快速地处理所有事件。它会测量在 poll() 中等待的时间与处理事件所花费时间的比值。轮询时间与总时间的比值称为“空闲”时间，即等待某些事件发生所花费的时间。该比值在统计页面的“idle”行或 CLI 中的“Idle_pct”字段中报告。当该值接近 100% 时，表示负载极低；当该值接近 0% 时，表示系统始终存在活动。尽管在系统过载时，由于其他进程可能抢占 HAProxy 进程的 CPU，该指标的准确性会降低，但它仍能较好地反映 HAProxy 对自身工作负载的判断：若负载较低但空闲比值也较低，可能表明 HAProxy 有大量工作需要处理，可能是由于需要处理非常耗时的规则。反之，若 HAProxy 显示空闲接近 100% 但处理速度仍然缓慢，说明它已处于等待传入数据的状态，无法采取任何措施加快处理速度。在以下示例中，HAProxy 完全处于空闲状态：

```shell
$ echo "show info" | socat - /var/run/haproxy.sock | grep ^Idle
Idle_pct: 100
```

当空闲比率开始变得非常低时，必须调整系统并正确配置进程和中断，以尽可能为所有任务节省 CPU 资源。如果存在防火墙，应尝试禁用它或进行调优，以确保其不会成为性能瓶颈的主要原因。请注意，卸载有状态防火墙通常会同时降低中断/软中断数量和系统使用率，因为此类防火墙同时作用于接收（Rx）和发送（Tx）路径。在 Linux 上，卸载 nf_conntrack 和 ip_conntrack 模块可判断是否存在优化空间。若存在优化空间，则模块以默认设置运行，需自行研究如何调优以获得更高性能。通常这涉及显著增大哈希表大小。在 FreeBSD 上，执行 "pfctl -d" 可同时禁用 "pf" 防火墙及其有状态引擎。

如果观察到大量时间消耗在中断（interrupt）或软中断（softirq）上，必须确保它们不运行在同一个 CPU 上。大多数系统倾向于将任务绑定到接收网络流量的 CPU，因为对于某些工作负载，这能提升性能。但面对高度依赖网络的工作负载时，情况恰恰相反，因为 HAProxy 进程将不得不与其内核对应部分竞争 CPU 资源。将 HAProxy 绑定到一个 CPU 核心，而将中断绑定到另一个核心，且两者共享相同的 L3 缓存，通常能显著提升网络性能。实践中，HAProxy 与网络栈的处理工作量非常接近，因此它们几乎可以各自填满一个完整的 CPU。在 Linux 上，可通过 taskset（用于 HAProxy）或使用 HAProxy 配置中的 cpu-map 实现此绑定，而中断的分配则在 /proc/irq. 中进行。许多网络接口支持多个队列和多个中断。通常，将它们分散到少量共享相同 L3 缓存的 CPU 核心上会有帮助。请务必停止 irq_balance，因为它在这些工作负载下总是执行最糟糕的调度策略。

对于涉及大量 SSL 流量或大量压缩的 CPU 密集型工作负载，使用多个进程专门处理特定任务可能是值得的，尽管此处并无通用规则，需通过实验进行验证。

为提升 CPU 处理能力，可将 HAProxy 配置为以多个进程运行，方法是在全局段中使用 "nbproc" 指令。但存在一些限制：

- 健康检查按进程运行，因此目标服务器将收到与运行进程数量相同的检查次数；
- maxconn 值和队列大小为进程级配置，必须正确设置以避免对服务器造成过载；
- 外部连接应避免使用端口范围，以防止端口冲突；
- stick-tables 为进程级配置，各进程之间不共享；
- 每个 peers 段在同一时间只能在一个进程中运行；
- CLI 操作在同一时间仅作用于单个进程。

基于此，通常最简单的配置方式是设置一个由多个进程运行的第一层，负责执行繁重的处理任务，并将流量转发至由单个进程运行的第二层。该机制适用于 SSL 和压缩，这两项功能均属于 CPU 密集型操作。实例可通过 Unix 套接字轻松串联（Unix 套接字比 TCP 套接字更高效，且不会占用端口），并利用 PROXY 协议向下一阶段传递客户端信息。采用此方式时，建议将所有单进程任务绑定至进程编号 1，其余任务绑定至后续进程，以便更方便地为不同机器生成相似的配置。

在 Linux 版本 3.9 及以上中，当每个进程在相同 IP:端口上绑定独立的监听套接字时，HAProxy 以多进程模式运行的效率会显著提高；这将使内核均匀地在所有进程间分发负载，而非唤醒所有进程。有关详细信息，请参阅配置手册中“bind”关键字行的“process”选项。
