--- title: "7. CPU 使用率" linkTitle: "7. CPU" weight: 310 description: "线程、CPU 亲和性、饱和度、profiling 及性能行为" icon: fa-solid fa-microchip module: [HAPROXY] categories: [任务] aliases: - /haproxy/management/cpu/ - /docs/haproxy/management/cpu/ - /haproxy/cpu/ upstream_link: "https://docs.haproxy.org/3.4/management.html" upstream_name: "HAProxy 3.4 Management Guide" upstream_ref: "v3.4.4, chapter 7" --- 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”选项。