HAProxy 在进行负载均衡时如何避免请求时间开销?
我测试了 HAProxy,并将其与用 Twisted (Python) 编写的简单端口转发器进行比较。在我的初步测试中,与直接向后端服务器发出 HTTP 请求相比,通过 HAProxy 负载均衡器发出 HTTP 请求不会增加请求时间的开销 [1]。而我自己的 python 脚本在响应时间上增加了约 3 倍的开销。
现在我的脚本是用 Python 编写的,HAProxy 用 C 语言编写的,因此,HAProxy 的优点是可以避免 Python 代码所经历的调用开销(从 Python 代码到系统调用)。但这是否可以解释性能上的巨大差异,或者 HAProxy 是否利用了一些操作系统技巧来进一步提高性能?我尝试分析我的 Python 代码,但它没有揭示 Python 代码中的任何热点,因此我猜测它大部分时间都花在分析中未考虑到的系统调用中。
[1]:据 ab 报告,有 100 个并发连接和 10,000 个总请求。 HAProxy 的平均时间是 37 毫秒,而我的 Python 脚本是 128 毫秒。
该设置是一个 TCP 负载均衡器,具有两个后端 Nodejs 服务器,仅提供静态文本。我故意想测试TCP负载平衡,然后测试协议变成了HTTP。这三台机器都是来自 Digital Ocean 的虚拟主机,单线程,512MB RAM,1 核。 Python 脚本可以在这里看到,我的 haproxy.cfg 可以在这里找到
HAProxy
网站已经涵盖了这个领域(我的错误忽略了它)。答案基本上是大量的低级优化。直接从HAProxy网站复制过来:
HAProxy 涉及操作系统架构中常见的几种技术,以实现绝对最大性能:
单进程、事件驱动的模型大大降低了上下文切换的成本和内存使用量。一毫秒内处理数百个任务是可能的,每个会话的内存使用量约为几千字节,而类似
Apache
的模型消耗的内存更多为每个进程的兆字节量级。O(1)
系统上的事件检查器(Linux
和 FreeBSD
)允许即时检测数万个连接上的任何事件。尽可能在读取和写入之间进行单缓冲,无需任何数据复制。这节省了大量的
CPU
周期和有用的内存带宽。通常,瓶颈是 I/O
和网络接口之间的 CPU
总线。在 10 Gbps
时,内存带宽也可能成为瓶颈。使用
splice()
下的 Linux
系统调用可以实现零拷贝转发,并从 Linux
3.5 开始实现真正的零拷贝。这允许小型 3 瓦以下设备(例如 Seagate Dockstar
)以 1 HTTP
转发 gigabit/s
流量。MRU
内存分配器使用固定大小的内存池进行即时内存分配,有利于热缓存区域而不是冷缓存区域。这极大地减少了创建新会话所需的时间。工作分解,例如一次多个
accept()
,以及在多进程模式下运行时限制每次迭代accept()
的数量,以便负载在进程之间均匀分配。基于树的存储,大量使用我多年来开发的
Elastic Binary
树。这用于保持计时器有序、保持运行队列有序、管理循环和最少连接队列,而只需 O(log(N))
成本。优化的
HTTP
标头分析:标头被动态解析和解释,并且解析被优化以避免重新读取任何先前读取的内存区域。当到达缓冲区末尾且标头不完整时,将使用检查点,这样当读取更多数据时,解析就不会从头开始重新开始。在 HTTP
上解析平均 Pentium-M 1.7 GHz
请求通常需要 2 微秒。小心减少昂贵的系统调用次数。默认情况下,大部分工作都是在用户空间完成的,例如时间读取、缓冲区聚合、文件描述符启用/禁用。