HAProxy是如何实现其高速的?

9 投票
1 回答
2613 浏览
提问于 2025-04-17 23:54

HAProxy是怎么避免在负载均衡时请求时间增加的呢?

我测试了HAProxy,并且为了好玩,把它和我用Twisted(Python)写的一个简单端口转发器进行了比较。在我的初步测试中,通过HAProxy负载均衡器发起HTTP请求,和直接向后端服务器发起HTTP请求相比,几乎没有增加请求时间的开销[1]。而我自己的Python脚本则增加了大约3倍的响应时间。

我的脚本是用Python写的,而HAProxy是用C语言写的,所以从理论上讲,HAProxy在调用时的开销要比Python代码少,因为Python代码需要经过一些系统调用。但这能解释性能差异这么大吗?还是说HAProxy利用了一些操作系统的技巧来进一步提升性能?我尝试分析我的Python代码,但并没有发现明显的性能瓶颈,所以我猜大部分时间都花在了没有被分析到的系统调用上。

[1]: 根据ab的报告,使用100个并发连接和10,000个总请求。HAProxy的平均时间是37毫秒,而我的Python脚本是128毫秒。

设置

这个设置是一个TCP负载均衡器,后面有两个nodejs服务器,只是提供静态文本。我特意想测试TCP负载均衡,所以测试协议就变成了HTTP。这三台机器都是来自Digital Ocean的虚拟主机,单线程,512MB内存,1个核心。 可以在这里查看Python脚本,而我的haproxy.cfg文件可以在这里找到

1 个回答

11

原来,HAProxy的网站已经讲得很清楚了(我之前没注意到)。简单来说,答案就是很多底层的优化。以下是直接从HAProxy网站上复制的内容:

HAProxy使用了几种在操作系统架构中常见的技术,以达到最佳性能:

  • 采用单进程、事件驱动的模型,这样可以大大减少上下文切换的成本和内存使用。它可以在一毫秒内处理几百个任务,而每个会话的内存使用量大约只有几千字节,而像Apache这样的模型每个进程的内存使用量通常在几兆字节。

  • 在支持的系统(如LinuxFreeBSD)上使用O(1)事件检查器,可以瞬间检测到成千上万条连接上的任何事件。

  • 尽可能使用单缓冲区,避免在读取和写入之间进行数据复制。这可以节省大量的CPU周期和有用的内存带宽。通常,瓶颈会出现在CPU和网络接口之间的I/O总线上。在10Gbps的情况下,内存带宽也可能成为瓶颈。

  • Linux下使用splice()系统调用实现零复制转发,从Linux 3.5开始,真正实现了零复制。这使得像Seagate Dockstar这样的小型设备也能以每秒一gigabit的速度转发HTTP流量。

  • 使用固定大小的内存池进行MRU内存分配,优先分配热缓存区域的内存,而不是冷缓存区域。这大大减少了创建新会话所需的时间。

  • 工作分配,比如可以同时进行多个accept()操作,并且在多进程模式下能够限制每次迭代的accept()数量,从而使负载在进程之间均匀分配。

  • 基于树的存储,充分利用我多年来开发的Elastic Binary树。这用于保持定时器有序,管理运行队列和轮询、最少连接队列,成本仅为O(log(N))

  • 优化HTTP头分析:头部信息在运行时被解析和解释,解析过程经过优化,避免重复读取之前已经读取的内存区域。当到达缓冲区末尾且头部不完整时,会使用检查点,这样在读取更多数据时,解析不会从头开始。解析一个平均的HTTP请求通常只需2微秒,在Pentium-M 1.7 GHz上。

  • 小心减少昂贵的系统调用数量。大部分工作默认在用户空间完成,比如读取时间、缓冲区聚合、文件描述符的启用/禁用。

撰写回答