本文档不提供任何配置方面的帮助或提示,但它解释了在哪里可以找到相关文档。下面的概述旨在帮助您按名称搜索章节并在文档中导航。致文档贡献者:本文档的格式为每行 80 列,缩进使用偶数个空格,不使用制表符。请严格遵守这些规则,以便在任何地方都能轻松打印。如果您添加章节,请更新下面的概述以便于搜索。
| 1. | 可用文档 | |
2. |
负载均衡和负载均衡器的快速入门 | |
3. |
HAProxy 简介 | |
| 3.1. | ||
| 3.2. | ||
| 3.3. | ||
| 3.3.1. | ||
| 3.3.2. | ||
| 3.3.3. | ||
| 3.3.4. | ||
| 3.3.5. | ||
| 3.3.6. | ||
| 3.3.7. | ||
| 3.3.8. | ||
| 3.3.9. | ||
| 3.3.10. | ||
| 3.3.11. | ||
| 3.3.12. | ||
| 3.3.13. | ||
| 3.3.14. | ||
| 3.3.15. | ||
| 3.3.16. | ||
| 3.4. | ||
| 3.4.1. | ||
| 3.4.2. | ||
| 3.4.3. | ||
| 3.5. | ||
| 3.6. | ||
4. |
配套产品和替代方案 | |
| 4.1. | ||
| 4.2. | ||
| 4.3. | ||
| 4.4. |
完整的 HAProxy 文档包含在以下文档中。请务必查阅相关文档以节省时间并获得最准确的答复。此外,请避免向邮件列表中提问,其答案已包含在这些文档中。 - intro.txt (本文档): 介绍负载均衡的基础知识、HAProxy 作为产品、它的功能、它的局限性、一些已知的陷阱、一些操作系统相关的限制、如何获取 HAProxy、它的发展、如何确保您运行的是所有已知修复版本、如何更新 HAProxy、补充和替代方案。 - management.txt: 解释如何启动 haproxy、如何在运行时管理它、如何在多个节点上管理它以及如何进行无缝升级。 - configuration.txt: 参考手册详细介绍了所有配置关键字及其选项。在需要更改配置时使用。 - coding-style.txt: 适用于希望向项目贡献代码的开发人员。它解释了代码应遵循的风格。它并不严格,并非所有代码都完全遵守,但过于偏离的代码将被拒绝。 - proxy-protocol.txt: 这是 PROXY 协议的事实规范,HAProxy 和许多第三方产品都实现了该协议。 - README: 如何从源代码构建 HAProxy。
负载均衡包括聚合多个组件,以实现高于每个组件单独容量的总处理能力,并且无需最终用户干预,且具有可扩展性。其结果是在一个组件执行一次操作所需的时间内,同时执行更多操作。然而,一次单操作仍然一次只在一个组件上执行,并且不会比没有负载均衡时更快。它总是需要至少与可用组件数量相同的操作以及一个有效的负载均衡机制来利用所有组件并充分受益于负载均衡。一个很好的例子是高速公路上的车道数量,它允许在同一时间段内通过更多的汽车,而不会提高它们的个人速度。负载均衡的示例: - 多处理器系统中的进程调度 - 链路负载均衡(例如,EtherChannel、Bonding) - IP 地址负载均衡(例如,ECMP、DNS 轮循) - 服务器负载均衡(通过负载均衡器) 执行负载均衡操作的机制或组件称为负载均衡器。在 Web 环境中,这些组件称为“网络负载均衡器”,更常见的名称是“负载均衡器”,因为这项活动是迄今为止最著名的负载均衡案例。负载均衡器可以 acting: - 在链路级别:这称为链路负载均衡,它包括选择将数据包发送到哪个网络链路; - 在网络级别:这称为网络负载均衡,它包括选择一系列数据包将遵循的路由; - 在服务器级别:这称为服务器负载均衡,它包括决定哪个服务器将处理连接或请求。存在两种不同的技术,它们满足不同的需求,尽管存在一些重叠。在每种情况下,重要的是要记住,负载均衡包括将流量从其自然流中转移出去,并且这样做始终需要最细致的关注,以在所有路由决策之间保持所需的**一致性**。第一种在数据包级别 acting,并或多或少地单独处理数据包。输入和输出数据包之间存在**一对一**的关系,因此可以使用常规网络嗅探器在负载均衡器的两侧跟踪流量。这种技术可以非常便宜且极其快速。它通常在硬件(ASIC)中实现,能够达到线速,例如执行 ECMP 的交换机。通常是无状态的,也可以是有状态的(考虑数据包所属的会话,称为**第四层 LB** 或 **L4**),如果数据包未被修改,则可能支持 **DSR**(直接服务器返回,无需再次通过 LB),但几乎不提供内容感知。这种技术非常适合网络级别负载均衡,尽管有时用于非常基本的服务器负载均衡,速度非常快。第二种 acting 在会话内容上。它要求将输入的流重新组装并作为一个整体进行处理。内容可能会被修改,输出流将被分割成新的数据包。因此,它通常由代理执行,并且通常被称为**第七层负载均衡器**或 **L7**。这意味着双方有两个不同的连接,并且输入和输出数据包的大小或数量之间没有关系。客户端和服务器不需要使用相同的协议(例如 IPv4 vs IPv6、明文 vs SSL)。操作总是**有状态**的,并且返回流量必须通过负载均衡器。额外的处理是有代价的,因此不总是能达到线速,尤其是在数据包较小的情况下。另一方面,它提供了广泛的可能性,并且通常通过纯软件实现,即使嵌入到硬件设备中。这种技术非常适合服务器负载均衡。基于数据包的负载均衡器通常以**切入模式**部署,因此它们安装在流量的正常路径上,并根据配置进行分流。返回流量不一定通过负载均衡器。可能会对网络目标地址进行一些修改,以将流量引导到正确的目的地。在这种情况下,返回流量**必须**通过负载均衡器。如果路由无法实现这一点,负载均衡器也可能将数据包的源地址替换为自己的地址,以强制返回流量通过它。基于代理的负载均衡器以其自己的 IP 地址和端口部署为服务器,无需架构更改。有时这需要对应用程序进行一些调整,以便客户端被正确地定向到负载均衡器的 IP 地址而不是直接定向到服务器的 IP 地址。一些负载均衡器可能需要调整某些服务器的响应才能实现这一点(例如,HTTP 中的 `Location` 标头字段用于 HTTP 重定向)。一些基于代理的负载均衡器可能会拦截它们不拥有的地址的流量,并在连接到服务器时欺骗客户端的地址。这使得它们可以像常规路由器或防火墙一样部署,以一种非常类似于基于数据包的负载均衡器的**切入模式**。这对于结合了数据包模式和代理模式的产品尤其受欢迎。在这种情况下,DSR 显然仍然不可能,并且返回流量仍然必须路由回负载均衡器。一个非常有伸缩性的分层方法包括拥有一个前端路由器,它接收来自多个负载均衡链路的流量,并使用 ECMP 将此流量分发到第一层的多个有状态的基于数据包的负载均衡器(L4)。这些 L4 负载均衡器又将流量传递给数量更多的基于代理的负载均衡器(L7),它们必须解析内容才能决定哪个服务器最终将接收流量。组件的数量和可能的流量路径增加了失败的风险;在非常大的环境中,即使有少数组件永久性故障并正在修复或替换,也是很正常的。**未充分考虑整个堆栈健康状况的负载均衡会严重降低可用性。** 出于这个原因,任何明智的负载均衡器都会验证它打算将流量发送到的组件是否仍然处于活动状态且可达,并且它将停止将流量发送到有故障的组件。这可以通过各种方法实现。最常见的方法包括定期发送**探测**以确保组件仍然正常运行。这些探测称为“**健康检查**”。它们必须能够代表要解决的故障类型。例如,基于 ping 的检查无法检测到 Web 服务器已崩溃并且不再监听端口,而连接到端口将验证这一点,更高级的请求甚至可以验证服务器仍然正常工作并且它依赖的数据库仍然可访问。健康检查通常涉及几次重试,以涵盖偶尔的测量错误。检查之间的间隔必须足够小,以确保有故障的组件在错误发生后不会被使用太久。其他方法包括**采样**生产流量**发送到目标,以观察其是否被正确处理,并驱逐返回不当响应的组件。然而,这需要牺牲一部分生产流量,并且并非总是可接受的。这两种机制的组合提供了**两全其美**,两者都用于检测故障,只有健康检查用于检测故障的结束。最后一种方法涉及**集中报告**:一个中央监控代理会定期向所有负载均衡器更新所有组件的状态。这为所有组件提供了基础设施的全局视图,尽管有时准确性或响应性较低。它最适合拥有许多负载均衡器和许多服务器的环境。第七层负载均衡器还面临另一个称为**粘性**或**持久性**的挑战。其原理是它们通常必须将来自同一源(例如最终用户)的多个后续请求或连接定向到同一目标。最著名的例子是在线商店的购物车。如果每次点击都导致一个新的连接,用户必须始终被发送到持有其购物车的服务器。内容感知使得更容易在请求中找到某些元素来识别要将其发送到的服务器,但这并不总是足够的。例如,如果将源地址用作选择服务器的键,则可以决定使用基于哈希的算法,并且给定 IP 地址将基于地址除以可用服务器数量的结果而始终发送到同一服务器。但是,如果一个服务器失败,结果会改变,所有用户都会突然被发送到另一台服务器并丢失他们的购物车。针对此问题的解决方案包括**记住所选的目标**,以便每次看到同一访问者时,无论可用服务器的数量如何,都会将其定向到同一服务器。信息可以存储在负载均衡器的内存中,在这种情况下,如果它不是单独的,它可能需要复制到其他负载均衡器,或者它可以存储在客户端的内存中,使用提供的各种方法,只要客户端能够每次请求都提供此信息(Cookie 插入、重定向到子域等)。此机制提供了不依赖不稳定或分布不均的信息(例如源 IP 地址)的额外好处。这实际上是采用第七层负载均衡器而不是第四层负载均衡器的最强原因。为了提取 Cookie、Host 标头字段、URL 或其他任何信息,负载均衡器可能需要**解密 SSL/TLS 流量**,甚至可能在将其传递给服务器时**重新加密**。这项昂贵的任务解释了为什么在某些高流量基础设施中,有时会有很多负载均衡器。由于第七层负载均衡器可以对流量执行许多复杂的操作(解密、解析、修改、匹配 Cookie、决定发送到哪个服务器等),它肯定会引起一些麻烦,并且非常普遍地被指责为许多它仅暴露出来的麻烦的根源。通常会发现服务器不稳定并周期性地上下线,或者对于 Web 服务器,它们返回的页面中包含硬编码的链接,迫使客户端直接连接到某个特定服务器而不经过负载均衡器,或者它们在高负载下需要很长时间才能响应导致超时。这就是为什么**日志记录**是第七层负载均衡的一个极其重要的方面。一旦报告了麻烦,就必须弄清楚负载均衡器是否做出了错误的决定,如果是,为什么会这样,以防止它再次发生。
HAProxy 写成“HAProxy”以指代产品,写成“haproxy”以指代可执行程序、软件包或进程。然而,两者通常都用于这两个目的,并且读作 H-A-Proxy。很早以前,“haproxy”代表“high availability proxy”,并且这个名字被写成两个独立的单词,尽管现在它只代表“HAProxy”。
HAProxy 是: - 一个 TCP 代理:它可以从监听套接字接受 TCP 连接,连接到服务器并将这些套接字连接在一起,从而允许双向流量流动; - 一个 HTTP 反向代理(在 HTTP 术语中称为“网关”):它将自己呈现为服务器,通过在监听 TCP 套接字上接受的连接接收 HTTP 请求,并将这些连接中的请求转发给使用不同连接的服务器。 - 一个 SSL 终止/发起/卸载器:SSL/TLS 可用于从客户端传入的连接、到服务器的连接,甚至两者上的连接。 - 一个 TCP 规范化器:由于连接由操作系统在本地终止,因此双方之间没有关联,因此异常流量(如无效数据包、标志组合、窗口广告、序列号、不完整连接(SYN Flood)等)不会传递到另一方。这可以保护脆弱的 TCP 堆栈免受协议攻击,并且还允许优化与客户端的连接参数,而无需修改服务器的 TCP 堆栈设置。 - 一个 HTTP 规范化器:配置为处理 HTTP 流量时,只传递有效且完整的请求。这可以防止许多基于协议的攻击。此外,协议偏差(规范中允许容忍的)将被修复,以免在服务器上造成问题(例如,多行标头)。 - 一个 HTTP 修复工具:它可以修改/修复/添加/删除/重写 URL 或任何请求或响应标头。这有助于修复复杂环境中的互操作性问题。 - 一个基于内容的开关:它可以考虑请求中的任何元素来决定将请求或连接传递给哪个服务器。因此,可以在同一个端口上处理多个协议(例如 HTTP、HTTPS、SSH)。 - 一个服务器负载均衡器:它可以负载均衡 TCP 连接和 HTTP 请求。在 TCP 模式下,负载均衡决策是针对整个连接做出的。在 HTTP 模式下,决策是针对每个请求做出的。 - 一个流量调节器:它可以在不同点应用一些速率限制,保护服务器免受过载,根据内容调整流量优先级,甚至通过标记数据包将此类信息传递给较低层和外部网络组件。 - 防止 DDoS 和服务滥用的保护:它可以维护每个 IP 地址、URL、Cookie 等的广泛统计数据,并检测滥用何时发生,然后采取行动(减慢攻击者、阻止他们、将他们发送到过时内容等)。 - 网络故障排除的观察点:由于日志中报告的信息精度高,因此通常用于缩小与网络相关的某些问题的范围。 - HTTP 压缩卸载器:它可以压缩服务器未压缩的响应,从而减少连接缓慢或使用高延迟移动网络的客户端的页面加载时间。HAProxy **不是**: - 一个显式的 HTTP 代理,即浏览器用来访问互联网的代理。有优秀的开源软件专为此任务而设计,例如 Squid。然而,HAProxy 可以安装在这种代理的前面,以提供负载均衡和高可用性。 - 一个缓存代理:它将按原样返回从服务器收到的内容,并且不会干扰任何缓存策略。有优秀的开源软件可用于此任务,例如 Varnish。HAProxy 可以安装在此类缓存的前面,以提供 SSL 卸载,并通过智能负载均衡实现可扩展性。 - 一个数据清理器:它不会修改请求或响应的正文。 - 一个 Web 服务器:在启动期间,它会将自身隔离在一个 chroot jail 中并放弃其特权,以便在启动后不执行任何文件系统访问。因此,它不能被转换为 Web 服务器。有优秀的开源软件可用于此,例如 Apache 或 Nginx,并且 HAProxy 可以安装在它们前面以提供负载均衡和高可用性。 - 一个基于数据包的负载均衡器:它看不到 IP 数据包或 UDP 数据报,也不会执行 NAT,甚至不会执行 DSR。这些是较低层的任务。一些基于内核的组件(如 IPVS (Linux Virtual Server))已经做得相当好,并且与 HAProxy 完美互补。
HAProxy 是一个单线程、事件驱动、非阻塞引擎,结合了非常快速的 I/O 层和基于优先级的调度程序。由于它旨在实现数据转发,因此其架构经过优化,以最少的操作尽可能快地移动数据。因此,它实现了一个分层模型,在每个级别提供绕过机制,确保数据在不需要时不会到达更高层。大部分处理在内核中执行,HAProxy 通过提供一些提示或避免某些可以稍后分组的操作,尽最大努力帮助内核尽可能快地完成工作。结果,典型数据显示,在 TCP 或 HTTP 关闭模式下,15% 的处理时间花费在 HAProxy 中,85% 花费在内核中,而在 HTTP 长连接模式下,HAProxy 占大约 30%,内核占 70%。一个进程可以运行多个代理实例;据报道,在单个进程中运行多达 300,000 个不同代理的配置运行良好。因此,通常不需要为所有实例启动多个进程。可以使 HAProxy 在多个进程上运行,但这有一些限制。通常,在 HTTP 关闭或 TCP 模式下没有意义,因为内核端对于某些操作(如 connect())的可伸缩性不太好。它在 HTTP 长连接模式下可伸缩性相当好,但单个进程可实现的性能通常会以数量级超出常见需求。然而,当用作 SSL 卸载器时,它是有意义的,并且在多进程模式下对这种功能有很好的支持。HAProxy 只需要 haproxy 可执行文件和一个配置文件即可运行。对于日志记录,强烈建议有一个配置正确的 syslog 守护进程和日志轮转。配置文件在启动前解析,然后 HAProxy 尝试绑定所有监听套接字,如果任何失败则拒绝启动。在此之后,它就无法再失败了。这意味着没有运行时失败,如果它接受启动,它将一直工作直到停止。一旦 HAProxy 启动,它只执行 3 件事情: - 处理传入连接; - 定期检查服务器状态(称为健康检查); - 与其他 haproxy 节点交换信息。处理传入连接是迄今为止最复杂的任务,因为它取决于许多配置可能性,但可以将其概括为以下 9 个步骤: - 从属于称为“前端”的配置实体的监听套接字接受传入连接,该前端引用一个或多个监听地址; - 将前端特定处理规则应用于这些连接,这可能导致阻塞它们、修改某些标头或拦截它们以执行统计页面或 CLI 等内部小程序; - 将这些传入连接传递给另一个代表服务器场(称为“后端”)的配置实体,该后端包含服务器列表和该服务器场的负载均衡策略; - 将后端特定处理规则应用于这些连接; - 根据负载均衡策略决定将连接转发到哪个服务器; - 将后端特定处理规则应用于响应数据; - 将前端特定处理规则应用于响应数据; - 发出日志以详细报告发生了什么; - 在 HTTP 中,返回到第二步以等待新请求,否则关闭连接。前端和后端有时被视为半代理,因为它们只查看端到端连接的一侧;前端只关心客户端,而后端只关心服务器。HAProxy 还支持**完整代理**,它们正是前端和后端的并集。当需要 HTTP 处理时,配置通常会分成前端和后端,因为它们提供了许多可能性,任何前端都可以将连接传递给任何后端。对于纯 TCP 代理,使用前端和后端很少有好处,并且使用完整代理可以使配置更具可读性。
本节将列举 HAProxy 实现的许多功能,其中一些是任何现代负载均衡器通常期望的,而另一些则是 HAProxy 架构的直接优势。更高级的功能将在下一节中详细介绍。
代理是将数据通过两个独立连接在客户端和服务器之间传输的操作。HAProxy 在代理和连接管理方面支持以下基本功能: - 为服务器提供干净的连接,以保护它们免受任何客户端缺陷或攻击; - 监听多个 IP 地址和/或端口,甚至端口范围; - **透明接受**:拦截针对任何不属于本地系统的任意 IP 地址的流量; - 服务器端口不需要与监听端口相关联,甚至可以按固定的偏移量进行转换(在范围设置时有用); - **透明连接**:根据需要欺骗客户端(或任何)IP 地址连接到服务器; - 在多站点 LB 中为服务器提供可靠的返回 IP 地址; - 通过缓冲区和可能的短期连接**卸载服务器**,以减少其并发连接数和内存占用; - **优化 TCP 堆栈**(例如 SACK)、拥塞控制,并减少 RTT 影响; - 支持双方不同的协议族(例如 IPv4/IPv6/Unix); - **超时强制执行**:HAProxy 支持多个级别的超时,具体取决于连接所处的阶段,因此死客户端或服务器、或攻击者不能长时间占用资源; - **协议验证**:检查 HTTP、SSL 或有效载荷,拒绝无效的协议元素,除非指示接受它们; - **策略强制执行**:确保只转发允许的内容; - 入站和出站连接都可以限制在特定的网络命名空间(仅限 Linux),这使得构建跨容器、多租户的负载均衡器变得容易; - PROXY 协议即使对于非 HTTP 流量,也能将客户端的 IP 地址呈现给服务器。这是 HAProxy 的一项扩展,已被许多第三方产品采纳,至少在撰写本文时包括以下产品: - 客户端:haproxy、stud、stunnel、exaproxy、ELB、squid - 服务器:haproxy、stud、postfix、exim、nginx、squid、node.js、varnish
HAProxy 的 SSL 堆栈根据 Google 工程师的说法,被认为是功能最强大的之一(http://istlsfastyet.com/)。最常用的功能使其相当完整,包括: - 基于 SNI 的多站点托管,对站点数量没有限制,并注重性能。已知有一个部署运行了 50,000 个域及其相应的证书; - 对通配符证书的支持减少了对许多证书的需求; - 基于证书的客户端身份验证,在未能提供有效证书时具有可配置的策略。例如,这允许呈现不同的服务器场来重新生成客户端证书; - 后端服务器的身份验证可确保后端服务器是真实的,而不是中间人; - 与后端服务器的身份验证让后端服务器知道连接到它的是预期的 haproxy 节点; - TLS NPN 和 ALPN 扩展使得能够可靠地卸载 SPDY/HTTP2 连接,并将它们以明文形式传递给后端服务器; - OCSP stapling 通过在客户端请求证书状态请求时内联提供 OCSP 响应,进一步减少了首次页面加载时间; - 动态记录大小调整既提供了高性能又提供了低延迟,并且通过允许浏览器在数据包仍在传输过程中开始获取新对象,显著减少了页面加载时间; - 永久访问所有相关的 SSL/TLS 层信息,用于日志记录、访问控制、报告等。这些元素可以嵌入到 HTTP 标头中,甚至作为 PROXY 协议的扩展,以便卸载的服务器能够获得如果它自己执行 SSL 终止所拥有的所有信息。 - 检测、记录和阻止某些已知的攻击,即使是在易受攻击的 SSL 库上,例如影响某些 OpenSSL 版本的 Heartbleed 攻击; - 支持无状态会话恢复(RFC 5077 TLS Ticket 扩展)。TLS 票证可以从 CLI 更新,这为它们提供了实现 Perfect Forward Secrecy 的方法,通过频繁轮换票证。
HAProxy 非常注重可用性。因此,它关心服务器的状态,并向其他网络组件报告自己的状态: - 服务器状态使用每个服务器的参数持续监控。这确保了通往服务器的路径对于常规流量是可操作的; - 健康检查支持 up 和 down 转换的两种迟滞,以防止状态抖动; - 检查可以发送到不同的地址/端口/协议:这使得检查被认为是其他服务器代表的单个服务变得容易,例如 HTTP+HTTPS 服务器的 HTTPS 端口; - 服务器可以跟踪其他服务器并同时宕机:这确保了托管多个服务的服务器可以原子化失败,并且没有人会被发送到部分失败的服务器; - 可以在服务器上部署代理来监控负载和健康状况:服务器可能希望报告其负载、运行状态、管理状态,独立于健康检查能看到的内容。通过在服务器上运行一个简单的代理,除了验证整个路径的健康检查之外,还可以考虑服务器自身对其健康的看法; - 提供各种检查方法:TCP 连接、HTTP 请求、SMTP hello、SSL hello、LDAP、SQL、Redis、send/expect 脚本,所有这些都可以带/不带 SSL; - 状态更改会在日志和统计页面中通知,并附带失败原因(例如,检测到失败时收到的 HTTP 响应)。在这种变化发生时,还可以向可配置的地址发送电子邮件; - 服务器状态也会在统计界面上报告,并可用于做出路由决策,以便根据其大小和/或健康状况(例如,城域网链路丢失)将流量发送到不同的服务器场; - HAProxy 可以使用健康检查请求将信息传递给服务器,例如它们的名称、权重、服务器场中其他服务器的数量等,以便服务器可以根据这些知识调整其响应和决策(例如,推迟备份以腾出更多 CPU); - 服务器可以使用健康检查来报告比简单的开/关更详细的状态(例如,“我想停止,请停止发送新访问者”); - HAProxy 本身可以将其状态报告给外部组件,例如路由器或其他负载均衡器,从而构建非常完整的**多路径**和**多层**基础设施。
与任何严肃的负载均衡器一样,HAProxy 非常重视可用性,以确保最佳的全局服务连续性: - **仅使用有效服务器**;其他服务器会自动从负载均衡服务器场中**移除**;但在某些条件下仍然可以强制使用它们; - 支持**优雅关闭**,以便可以在不影响任何连接的情况下将服务器从服务器场中移除; - **备用服务器**在活动服务器宕机时会自动使用并替换它们,从而尽可能不丢失会话。这还允许构建到达同一服务器的**多个路径**(例如,多个接口); - 在服务器场中**too many servers are down** 时,能够返回全局失败状态。这与监控能力相结合,使得上游组件能够为给定服务选择不同的 LB 节点; - **无状态设计**便于构建集群:HAProxy 通过设计,尽最大努力确保最高的服务连续性,而无需存储在发生故障时可能丢失的信息。这确保了接管尽可能无缝; - **与标准的 VRRP 守护进程 keepalived 集成良好**:HAProxy 可以轻松地告知 keepalived 其状态,并且非常适合浮动的虚拟 IP 地址。注意:仅在基于集群的解决方案(Heartbeat、...)之上使用 IP 冗余协议(VRRP/CARP),因为它们提供了最快速、最无缝、最可靠的切换。
HAProxy 提供了一套相当完整的负载均衡功能,其中大部分功能在许多其他负载均衡产品中都无法获得: - 支持不少于 9 种负载均衡算法,其中一些算法可以应用于输入数据,提供无限的可能性。最常见的有:轮询(round-robin)(适用于短连接,依次选择服务器)、最少连接(leastconn)(适用于长连接,选择连接数最少且最近最少使用的服务器)、源地址(source)(适用于 SSL 农场或终端服务器农场,服务器直接取决于客户端的源地址)、URI(适用于 HTTP 缓存,服务器直接取决于 HTTP URI)、HDR(服务器直接取决于特定 HTTP 头部字段的内容)、首选(first)(适用于短生命周期的虚拟机,所有连接都集中在最小可能的服务器子集上,以便关闭未使用的服务器); - 以上所有算法都支持每台服务器的权重,以便能够容纳不同代的服务器,或将一小部分流量导向特定服务器(调试模式,运行软件的下一个版本等); - 支持动态权重,适用于轮询、最少连接和一致性哈希;这允许在运行时通过 CLI 甚至服务器上的代理修改服务器权重; - 支持慢启动,只要支持动态权重;这允许服务器逐步接受流量。这对于需要运行时编译类以及需要填充冷缓存才能全速运行的脆弱应用服务器来说是一项重要功能; - 哈希可以应用于各种元素,如客户端的源地址、URL 组件、查询字符串元素、头部字段值、POST 参数、RDP cookie; - 一致性哈希可以保护服务器农场在添加或移除服务器时免受大规模重新分配的影响。这对于大型缓存农场非常重要,并且允许使用慢启动来重新填充冷缓存; - 一系列内部指标,如每台服务器、每个后端连接数,后端可用连接槽数等,使得构建非常高级的负载均衡策略成为可能。
没有粘性(stickiness),应用负载均衡将毫无意义。HAProxy 提供了相当全面的选项来保持访客连接到同一台服务器,即使在服务器添加/移除、正常/宕机循环等各种事件中,并且其中一些方法被设计成能够抵抗多个负载均衡节点之间的距离,因为它们不需要任何复制: - 如果需要,粘性信息可以从不同位置单独匹配和学习。例如,JSESSIONID cookie 可以在 cookie 和 URL 中同时匹配。最多可以同时学习 8 个并行源,每个源都可以指向不同的粘性表(stick-table); - 粘性信息可以来自请求或响应中可以看到的任何内容,包括源地址、TCP 负载偏移和长度、HTTP 查询字符串元素、头部字段值、cookie 等。 - 粘性表(stick-tables)在多主模式下在所有节点之间复制; - SSL-ID 或 RDP cookie(用于 TSE 农场)等常用元素可以直接访问,方便操作; - 所有粘性规则都可以由 ACL(访问控制列表)动态条件化; - 可以决定不粘连某些服务器,例如备份服务器,以便当主服务器恢复时,它会自动接管流量。这在多路径环境中经常使用; - 在 HTTP 中,通常倾向于不学习任何内容,而是操作一个专用于粘性的 cookie。为此,可以检测、重写、插入或添加前缀来让客户端记住分配的服务器; - 服务器可以决定在注销时更改或清除粘性 cookie,以便离开的访客自动解除与服务器的绑定; - 使用基于 ACL 的规则,还可以选择性地忽略或强制粘性,而不管服务器状态如何;结合高级健康检查,这有助于管理员在将服务器面向全世界之前验证其正常运行; - 一种创新的机制,用于设置 cookie 的最大空闲时间和持续时间,确保粘性可以平滑地在永不关闭的设备(智能手机、电视、家用电器)上停止,而无需将其存储在持久存储中; - 多个服务器条目可以共享相同的粘性键,这样在多路径环境中,当一条路径断开时,粘性也不会丢失; - 软停止(soft-stop)确保只有具有粘性信息的用户将继续访问他们被分配到的服务器,而新用户将不会被导向那里。
HAProxy 支持使用一套广泛的“样本提取函数”进行信息采样。其原理是将已知为样本的信息提取出来,供即时使用。这用于粘性、构建条件、在日志中生成信息或丰富 HTTP 头部。样本可以从各种来源提取: - 常量:整数、字符串、IP 地址、二进制块; - 进程:日期、环境变量、服务器/前端/后端/进程状态、字节/连接计数/速率、队列长度、随机生成器等; - 变量:每个会话、每个请求、每个响应的变量; - 客户端连接:源和目标地址和端口,以及所有相关的统计计数器; - SSL 客户端会话:协议、版本、算法、加密套件、密钥大小、会话 ID、所有客户端和服务器证书字段、证书序列号、SNI、ALPN、NPN、客户端对某些扩展的支持; - 请求和响应缓冲区内容:偏移量/长度处的任意负载、数据长度、RDP cookie、SSL hello 类型解码、TLS SNI 解码; - HTTP(请求和响应):方法、URI、路径、查询字符串参数、状态码、头部值、位置头部值、cookie、捕获、认证、正文元素; 样本随后可以经过一系列称为“转换器”(converters)的操作进行转换。转换器消耗一个样本并生成一个新的样本,可能是完全不同的类型。例如,转换器可以仅返回输入字符串的整数长度,或者将字符串转换为大写。在最终使用之前,可以对样本应用任意数量的转换器。在所有可用的样本转换器中,以下是最常用的: - 算术和逻辑运算符:它们可以对输入数据进行高级计算,例如计算比率、百分比或简单地从一个单位转换为另一个单位; - IP 地址掩码在需要按较大网络对某些地址进行分组时很有用; - 数据表示:URL 解码、base64、十六进制、JSON 字符串、哈希; - 字符串转换:从固定位置、固定长度提取子字符串,围绕特定分隔符提取字段,提取特定单词,更改大小写,应用基于正则表达式的替换; - 日期转换:转换为 HTTP 日期格式,将本地时间转换为 UTC 并反之,添加或删除偏移量; - 在粘性表中查找条目以获取统计信息或分配的服务器; - 基于映射的文件进行键到值的转换(主要用于地理位置)。
映射是一种强大的转换器类型,它通过在启动时将一个两列文件加载到内存中,然后从第一列查找每个输入样本,如果找到条目,则返回第二列中的相应模式,否则返回默认值。输出信息也是一个样本,它可以经历其他转换,包括其他映射查找。映射最常用于将客户端的 IP 地址转换为 AS 号或国家代码,因为它们支持网络地址的最长匹配,但它们可以用于各种其他目的。它们的部分优势在于它们可以从 CLI 或通过其他样本的某些操作进行动态更新,从而使它们能够存储和检索后续访问之间的信息。另一个优势来自基于二进制树的索引,这使得它们即使包含数十万个条目也极其快速,从而使地理位置的设置非常便宜且容易。
HAProxy 中的大多数操作都可以进行条件化。条件通过使用逻辑运算符(AND、OR、NOT)组合多个 ACL 来构建。每个 ACL 是一系列测试,基于以下元素: - 用于检索待测试元素的样本提取方法; - 可选的转换器系列,用于转换元素; - 一系列要匹配的模式; - 用于指示如何将模式与样本进行比较的匹配方法。例如,样本可以从 HTTP "Host" 头部提取,然后转换为小写,然后使用正则表达式匹配方法与多个正则表达式模式进行匹配。从技术上讲,ACL 是基于与映射相同的核心构建的,它们共享完全相同的内部结构、模式匹配方法和性能。唯一的真正区别是,它们不返回样本,而是只返回“找到”或“未找到”。在使用方面,ACL 模式可以在配置文件中内联声明,而不需要自己的文件。ACL 可以命名以便于使用或使配置更易理解。一个命名 ACL 可以声明多次,它将依次评估所有定义,直到有一个匹配。提供了大约 13 种不同的模式匹配方法,包括 IP 地址掩码、整数范围、子字符串、正则表达式。它们像函数一样工作,就像任何编程语言一样,只评估需要的部分,所以当涉及 OR 的条件已经为真时,后续的条件将不再评估,同样当涉及 AND 的条件已经为假时,剩余的条件也不会被评估。声明的 ACL 数量没有实际限制,并且提供了一些常用 ACL。然而,经验表明,使用大量命名 ACL 的设置很难排查,有时内联使用匿名 ACL 更容易,因为它需要的外部引用较少。
HAProxy 实现了一种称为基于内容的切换(content-based switching)的机制。其原理是连接或请求到达一个前端,然后处理该请求或连接携带的信息,此时可以编写基于 ACL 的条件,利用这些信息来决定哪个后端将处理该请求。因此,流量根据请求的内容被定向到一个后端或另一个后端。最常见的例子是使用 Host 头部和/或路径中的元素(子目录或文件名扩展名)来决定 HTTP 请求是针对静态对象还是应用程序,并将静态对象的流量路由到由快速轻量级服务器组成的后端,其余所有流量路由到更复杂的应用程序服务器,从而构成一种精细的虚拟主机解决方案。这对于让多种技术共存作为更全面的解决方案非常方便。内容切换的另一个用例是根据各种标准使用不同的负载均衡算法。缓存可以使用 URI 哈希,而应用程序可以使用轮询。最后但同样重要的是,它允许多个客户使用一部分共享资源,通过强制实施每个后端(即每个客户机连接限制)。内容切换规则的可伸缩性非常好,尽管它们的性能可能取决于 ACL 的数量和复杂性。但也可以编写动态内容切换规则,其中样本值直接转换为后端名称,而根本不使用 ACL。据报道,这种配置在生产环境中至少与 300,000 个后端一起运行良好。
粘性表(stick-tables)常用于存储粘性信息,即保留某个访问者被定向到的服务器的引用。键是与访问者关联的标识符(其源地址、连接的 SSL ID、HTTP 或 RDP cookie、从 URL 或负载中提取的客户编号等),存储的值是服务器的标识符。粘性表可以使用 3 种不同类型的样本作为其键:整数、字符串和地址。每个代理只能引用一个粘性表,并在所有地方都用代理名称指定。最多可以并行跟踪 8 个键。服务器标识符在请求或响应处理过程中确定,一旦键和服务器都被知道。粘性表的内容可以在主动-主动模式下与其他称为“对等体”(peers)的 HAProxy 节点以及在重载期间的新进程之间复制,这样所有负载均衡节点共享相同的信息并在客户端请求分散到多个节点时做出相同的路由决策。由于粘性表是基于识别客户端的因素进行索引的,因此它们也常用于存储额外信息,例如每个客户端的统计数据。额外的统计数据占用额外空间,需要显式声明。可存储的统计数据类型包括输入和输出带宽、并发连接数、一段时间内的连接速率和计数、错误量和频率、一些特定的标签和计数器等。为了支持存储此类信息而不强制粘连到给定服务器,实现了一种特殊的“跟踪”(tracking)功能,允许同时跟踪来自不同表的最多 3 个并发键,而不管粘性规则。每个存储的统计信息都可以通过 CLI 搜索、转储和清除,并增加了实时故障排除功能。虽然此机制可用于跟踪回访用户或根据好坏行为调整提供的服务质量,但它主要用于对抗服务滥用,更普遍地用于 DDoS,因为它允许构建复杂模型以高速处理来检测某些不良行为。
在很多地方,HAProxy 需要操作字符串,例如日志、重定向、添加标头等等。为了提供最大的灵活性,引入了格式化字符串的概念,最初是为了日志记录的目的,这也解释了为什么它仍然被称为“log-format”。这些字符串包含转义字符,允许将各种动态数据(包括变量和样本获取表达式)引入字符串中,甚至可以在结果转换为字符串时调整编码(例如,添加引号)。这提供了一种强大的方式来构建标头内容或自定义日志行。此外,为了保持构建最常见字符串的简单性,提供了大约 50 个特殊标签作为日志中常用信息的快捷方式。
在应用程序未为负载均衡设计的情况下,在其前面安装负载均衡器可能是一项具有挑战性的任务,如果没有合适的工具。在这种情况下,最常要求的操作之一是调整请求和响应头部,以使负载均衡器看起来像原始服务器,并修复硬编码的信息。这包括更改请求中的路径(强烈不建议这样做)、修改 Host 头部字段、修改重定向的 Location 响应头部字段、修改 cookie 的路径和域属性等等。还有一些服务器可能非常冗长,倾向于在响应中泄露过多信息,使其更容易受到定向攻击。虽然理论上清理这些不是负载均衡器的职责,但实际上它处于基础设施中的最佳位置,可以确保所有内容都被清理干净。同样,有时负载均衡器将不得不拦截某些请求并以重定向到新目标 URL 的响应。虽然有些人倾向于混淆重定向和重写,但这两个是完全不同的概念,因为重写使客户端和服务器看到不同的内容(并且在访问页面的位置上不一致),而重定向要求客户端访问新的 URL,以便它看到与服务器相同的位置。为了做到这一点,HAProxy 支持各种重写和重定向的可能性,其中包括: - 基于正则表达式的请求和响应的 URL 和头部重写。正则表达式是修改头部值最常用的工具,因为它们易于操作且广为人知; - 头部也可以基于格式化字符串进行附加、删除或替换,以便在那里传递信息(例如,客户端 TLS 算法和加密套件); - HTTP 重定向可以使用任何 3xx 代码重定向到相对、绝对或完全动态(格式化字符串)的 URI; - HTTP 重定向还支持一些额外选项,例如设置或清除特定 cookie、删除查询字符串、在缺少时添加斜杠等; - 所有操作都支持基于 ACL 的条件;
HAProxy 尽最大努力最大化服务可用性,并为此付出了巨大的努力来保护服务器免受过载和攻击。第一也是最重要的点是,只有完整且有效的请求才会被转发到服务器。最初的原因是 HAProxy 需要找到它需要保持与字节流同步的协议元素,第二个原因是直到请求完成,都无法知道某些元素是否会改变其语义。这直接带来的好处是服务器不会暴露在无效或不完整的请求之下。这是对 slowloris 攻击的一种非常有效的防护,这种攻击对 HAProxy 几乎没有影响。另一个重要点是 HAProxy 包含缓冲区来存储请求和响应,并且仅在请求完成后才将其发送到服务器,并通过从本地网络快速读取整个响应,服务器端的连接使用时间非常短,从而尽可能地保留服务器资源。这直接扩展到 HAProxy 可以人为地限制到服务器的并发连接数或未完成请求数,这保证了即使在流量高峰期间服务器以 100% 的容量运行,也不会过载。所有过多的请求都将被排队等待处理。最终,这种巨大的资源节省通常可以确保更好的服务器响应时间,其速度甚至比服务器过载还要快。排队的请求可能会被重新调度到其他服务器,甚至在客户端中止时被取消,这也保护了服务器免受“重新加载效应”的影响,即访客在页面加载缓慢时每次点击“重新加载”都会产生新请求,并使服务器处于过载状态。慢启动机制还可以在服务器仍在完成启动或编译类时,保护重启中的服务器免受高流量水平的影响。关于协议级别的保护,可以放宽 HTTP 解析器以接受非标准但无害的请求或响应,甚至修复它们。这使得在开发修复程序时可以访问错误的应用程序。同时,恶意消息被完全捕获并附有详细报告,帮助开发人员发现应用程序中的问题。最危险的协议违规被正确检测、处理和修复。例如,有两个 Content-length 头的格式错误的请求或响应,如果值完全相同,则会被修复,如果值不同,则会被拒绝,因为这会成为安全问题。协议检查不仅限于 HTTP,也适用于 TLS 或 RDP 等其他协议。当检测到协议违规或攻击时,有多种响应用户的选项,例如返回常见的“HTTP 400 bad request”、以 TCP 重置关闭连接,或在长时间延迟后伪造错误(“tarpit”)来混淆攻击者。所有这些都有助于保护服务器,通过劝阻攻击客户端放弃维护成本高昂的攻击。HAProxy 还提供了一些更高级的选项来防止意外的数据泄露和会话交叉。它不仅可以记录可疑的服务器响应,还可以记录并选择性地阻止可能影响给定访问者保密性的响应。例如,一个可缓存的 cookie 出现在一个可缓存的响应中,这可能导致中间缓存将其提供给另一个访问者,从而导致意外的会话共享。
日志是负载均衡器的极其重要的功能,首先是因为负载均衡器常常被错误地指责导致其揭示的问题,其次是因为它位于基础设施的关键点,需要分析所有正常和异常活动并与其他组件相关联。HAProxy 提供非常详细的日志,精确到毫秒,并且可以精确到连接接受时间,可以在防火墙日志中进行搜索(例如,用于 NAT 相关性)。默认情况下,TCP 和 HTTP 日志相当详细,包含故障排除所需的一切,例如源 IP 地址和端口、前端、后端、服务器、计时器(请求接收持续时间、队列持续时间、连接建立时间、响应头部时间、数据传输时间)、全局进程状态、连接计数、队列状态、重试计数、详细的粘性操作和断开原因、具有安全输出编码的头部捕获。然后可以扩展或替换此格式以包含任何采样数据、变量、捕获,从而产生非常详细的信息。例如,可以记录客户端的累积请求数或访问的不同 URL 数。可以使用标准的 ACL 为每个请求调整日志级别,因此可以自动静默某些被视为污染的日志,并在部分流量发生异常行为时发出警告(例如,源地址的 URL 过多或 HTTP 错误)。还发出具有自己级别的管理日志,例如告知服务器丢失或恢复。每个前端和后端可以使用多个独立的日志输出,这便于多租户。日志首选通过 UDP 发送,可能是 JSON 编码,并在可配置的行长度后截断,以保证传输。
HAProxy 提供一个基于 Web 的统计报告界面,具有身份验证、安全级别和范围功能。因此,可以为每个托管的客户提供他自己的页面,只显示他自己的实例。这个页面可以位于常规网站的一个隐藏 URL 部分,这样就不需要打开新的端口。这个页面还可以报告其他 HAProxy 节点的可用性,以便可以一目了然地发现一切是否按预期工作。视图是综合性的,可以访问许多详细信息(例如错误原因、最后访问和最后更改持续时间等),这些信息也可以作为 CSV 表格访问,其他工具可以导入该表格来绘制图表。该页面可以自动刷新,用作大型显示器上的监控页面。在管理模式下,该页面还允许更改服务器状态,以方便维护操作。
HAProxy 设计用于在常规生产环境中保持极高的稳定性和安全性。它提供为单个可执行文件,无需安装过程。可以轻松共存多个版本,这意味着可以通过重要性顺序逐步升级实例(并且推荐这样做),而不是一次性迁移所有实例。配置文件易于版本控制。配置检查是离线进行的,因此不需要重新启动可能失败的服务。在配置检查期间,可以检测到许多高级错误(例如,一个隐藏另一个的规则,或无效的粘性),并提供详细的警告和配置提示来修复它们。向后兼容性非常长,版本 1.5 仍然完全支持 13 年前编写的版本 1.1 的配置,而 1.6 只删除了一些几乎未使用、已过时的关键字。配置和软件升级机制平滑且非中断性,允许旧进程和新进程在系统上共存,每个进程处理自己的连接。系统状态、构建选项和库兼容性在启动时报告。一些高级功能允许应用程序管理员平滑地停止服务器,检测到它上面没有任何活动后,将其下线、停止、升级,并确保它在升级过程中不接受任何流量,然后通过正常路径再次测试它,而无需将其公开给公众,而且这一切都不需要触碰 HAProxy。这确保了即使复杂的操作也可以在营业时间内完成,并且所有技术资源都可用。该过程尽量节省资源,使用内存池来节省分配时间并限制内存碎片,一旦其内容发送完毕就释放负载缓冲区,并支持强制执行严格的内存限制,超出该限制时连接必须等待缓冲区可用而不是分配更多内存。此系统有助于在某些严格的环境中保证内存使用。通过 UNIX 或 TCP 套接字提供命令行界面 (CLI),用于执行多项操作并检索故障排除信息。在此套接字上执行的所有操作都不需要更改配置,因此主要用于临时更改。使用此接口可以更改服务器的地址、权重和状态,咨询统计信息并清除计数器,转储和清除粘性表,可能通过键标准选择性地清除,转储和终止客户端和服务器连接,转储捕获的错误并详细分析错误的確切原因和位置,转储、添加和删除 ACL 和映射条目,更新 TLS 共享密钥,对任意前端应用连接限制和速率限制(在共享托管环境中很有用),以及禁用特定前端以释放监听端口(在白天操作被禁止但仍需要修复时很有用)。对于强制要求 SNMP 的环境,至少存在两个代理,一个随 HAProxy 源代码提供,并依赖 Net-SNMP Perl 模块。另一个随商业软件包提供,不需要 Perl。两者的覆盖范围大致相当。通常建议在部署 HAProxy 的机器上安装 4 个实用程序: - socat(用于连接 CLI,尽管某些 netcat 分支在某种程度上也可以做到); - halog(来自最新的 HAProxy 版本):这是日志分析工具,它以极快的速度(每秒 1 到 2 GB)解析原生的 TCP 和 HTTP 日志,并提取有用的信息和统计数据,例如每个 URL 的请求数、每个源地址的请求数、按响应时间或错误率排序的 URL、终止代码等。它被设计用于部署在生产服务器上以帮助实时故障排除,因此它必须在那里随时可用; - tcpdump:强烈推荐使用它来获取排除日志可见问题所需的网络跟踪。在应用程序分析和 haproxy 分析之间存在一个分歧点,网络跟踪是确定谁对谁错的唯一方法。通过 tcpdump 还可以很容易地检测到网络堆栈和虚拟机监控程序中的错误; - strace:它是 tcpdump 的伴侣。它将报告 HAProxy 实际看到的内容,并有助于区分操作系统负责的问题和 HAProxy 负责的问题。当怀疑 HAProxy 中存在 bug 时,通常会要求使用 strace;
根据 HAProxy 部署的操作系统,可能提供或需要某些额外的功能。虽然它支持多种平台,但 HAProxy 主要在 Linux 上开发,这解释了为什么某些功能仅在该平台上可用。透明绑定(transparent bind)和连接(connect)功能、将连接绑定到特定网络接口的支持,以及将多个进程绑定到同一 IP 地址和端口的能力仅在 Linux 和 BSD 系统上可用,尽管只有 Linux 执行内核级别的负载均衡,将传入请求在可用进程之间分配。在 Linux 上,还有许多额外的功能和优化,包括对网络命名空间(也称为“容器”)的支持,允许 HAProxy 成为所有容器之间的网关,能够在客户端连接上设置 MSS、Netfilter 标记和 IP TOS 字段,支持监听端上的 TCP FastOpen,TCP 用户超时允许内核在检测到客户端在配置的超时之前消失时快速杀死连接,TCP 拼接允许内核在连接的两端之间转发数据,从而避免多次内存复制,能够启用“延迟接受”(defer-accept)绑定选项,以便在内核缓冲区中有数据可用时才接收到传入连接的通知,以及能够发送带有确认连接的 ACK(有时称为“捎带”)的请求,这通过“tcp-smart-connect”选项启用。在 Linux 上,HAProxy 还非常注重操作 TCP 延迟 ACK,以尽可能多地节省网络上的数据包。有些系统的时钟不可靠,会来回跳动。这曾经在一些 NUMA 系统中发生过,其中多个处理器看不到完全相同的时间,最近在虚拟化环境中变得更加普遍,虚拟时钟与实际时钟无关,导致巨大的时间跳跃(有时观察到高达 30 秒)。这导致了许多关于超时强制执行的问题。由于这些系统的缺陷,HAProxy 维护自己的单调时钟,它基于系统时钟,但漂移会被测量和补偿。这确保了即使系统时钟非常差,计时器也能保持合理的准确性,并且超时继续工作。请注意,这个问题影响了在该系统上运行的所有软件,并且不特定于 HAProxy。常见的影响是虚假的超时或应用程序冻结。因此,如果在此类系统上检测到此行为,则必须修复,而不管 HAProxy 如何自我保护。
HAProxy 可以通过 Lua 嵌入式语言进行构建,这为复杂的请求或响应操作、路由决策、统计处理等领域开辟了广泛的新可能性。使用 Lua 甚至可以建立到其他服务器的并行连接以交换信息。这样,例如,开发身份验证系统就成为可能(尽管很复杂)。有关如何使用 Lua 的更多信息,请参阅文件“doc/lua-api/index.rst”中的文档。
典型的 CPU 使用率显示,在 TCP 或 HTTP 关闭模式下,15% 的处理时间花费在 HAProxy 中,85% 花费在内核中;在 HTTP Keep-Alive 模式下,大约 30% 花费在 HAProxy 中,70% 花费在内核中。这意味着操作系统及其调优对整体性能有很大影响。使用情况因用户而异,有些人关注带宽,有些人关注请求速率,有些人关注连接并发性,有些人关注 SSL 性能。本节旨在提供一些有助于此任务的要素。需要牢记的是,每项操作都有成本,因此每项单独的操作都会在其之上增加开销,在某些情况下可以忽略不计,而在其他情况下则可能占主导地位。处理连接中的请求时,我们可以说: - 转发数据比解析请求或响应头部成本低; - 解析请求或响应头部比建立然后关闭到服务器的连接成本低; - 建立和关闭连接比 TLS 恢复操作成本低; - TLS 恢复操作比完整的 TLS 握手(带有密钥计算)成本低; - 空闲连接比包含数据的缓冲区连接成本低; - TLS 上下文比包含数据的连接消耗更多内存。因此,实际上,处理负载字节比处理头部字节便宜,因此通过大对象(每单位体积请求数少)比小对象(每单位体积请求数多)更容易实现高网络带宽。这解释了为什么最大带宽始终用大对象测量,而请求速率或连接速率用小对象测量。一些操作可以在分布在多个 CPU 上的多个进程中很好地扩展,而另一些则不能很好地扩展。网络带宽的扩展性不好,因为对于大对象,CPU 很少是瓶颈,主要是网络带宽和数据总线才能到达网络接口。由于处理本地端口表时系统中的一些锁,连接速率在多个处理器上的扩展性不好。持久连接的请求速率扩展性非常好,因为它不涉及太多内存或网络带宽,并且不需要访问锁定的结构。TLS 密钥计算扩展性非常好,因为它是完全 CPU 绑定的。TLS 恢复扩展性适中,但在大约 4 个进程后达到极限,因为访问共享表的开销抵消了从更多电源获得的微小收益。在经过良好调优的系统中,可以预期性能数字在以下范围内。重要的是将它们视为数量级,并期望根据处理器、IRQ 设置、内存类型、网络接口类型、操作系统调优等产生显著差异。以下数字是在运行频率为 3.7 GHz 的 Core i7 上测得的,该处理器配备了双端口 10 Gbps NIC,运行 Linux 内核 3.10、HAProxy 1.6 和 OpenSSL 1.0.2。HAProxy 作为单个进程运行在单个专用 CPU 核心上,另外两个核心专用于网络中断: - 清文本大对象(256 kB 或更高)的最大网络带宽为 20 Gbps,中等对象(41kB 或更高)为 10 Gbps; - 使用 AES256-GCM 加密套件处理大对象时的 TLS 流量为 4.6 Gbps; - 每秒 83,000 个 TCP 连接(从客户端到服务器); - 每秒 82,000 个 HTTP 连接(从客户端到服务器); - 每秒 97,000 个 HTTP 请求(服务器关闭模式,客户端保持连接,服务器关闭); - 每秒 243,000 个 HTTP 请求(端到端保持连接模式); - 每秒 300,000 个过滤的 TCP 连接(反 DDoS); - 每秒 160,000 个 HTTPS 请求(保持连接模式,通过持久 TLS 连接); - 每秒 13,100 个 HTTPS 请求(使用 TLS 恢复连接); - 每秒 1,300 个 HTTPS 连接(使用 RSA2048 重协商的 TLS 连接); - 每 GB RAM 可容纳 20,000 个并发饱和连接(包括系统内存缓冲区的内存);经过仔细调优可以做得更好,但这个结果很容易实现。 - 每 GB RAM 可容纳大约 8,000 个并发 TLS 连接(仅客户端,包括系统内存缓冲区的内存); - 每 GB RAM 可容纳大约 5,000 个并发端到端 TLS 连接(双向,包括系统内存缓冲区的内存); 因此,一个经验法则是,TLS 保持连接到 TLS 恢复,以及 TLS 恢复到 TLS 重协商之间的请求速率会除以 10,而 HTTP 保持连接到 HTTP 关闭之间的请求速率只会除以 3。另一个好经验法则是,一个具有 AES 指令的高频核心每核心可以处理大约 5 Gbps 的 AES-GCM。拥有更多核心很少有帮助(TLS 除外),而且由于频率较低,反而会适得其反。总的来说,少量高频核心更好。另一个好经验法则是,在同一台服务器上,HAProxy 将能够饱和: - 大约 5-10 个静态文件服务器或缓存代理; - 大约 100 个防病毒代理; - 大约 100-1000 个应用程序服务器,具体取决于所使用的技术。
HAProxy 是一个遵循 GPLv2 许可的开源项目,这意味着在提供源代码的前提下,允许任何人重新分发它,尤其是在进行修改之后。HAProxy 以一个名为“master”或“mainline”的主要开发分支进行演进,当代码被认为稳定后,新的分支将从中派生出来。许多网站出于参与项目或需要最新功能的目的,会自愿在生产环境中使用一些开发分支,他们的反馈对于修复 bug 和评估正在开发版本整体的质量和稳定性非常有价值。当代码足够稳定后创建的新分支构成了一个稳定版本,并且通常会维护数年,因此即使不在最新版本上,也没有急迫需要迁移到更新分支。一旦发布了一个稳定分支,它可能只会收到 bug 修复,并且极少数情况下会进行小的功能更新,以使用户的生活更轻松。所有进入稳定分支的修复都必须来自 master 分支。这保证了升级后不会丢失任何修复。因此,如果您修复了一个 bug,请针对 master 分支提交补丁,而不是稳定分支。您甚至可能会发现它已经被修复了。这个过程还确保了稳定分支中的回归极为罕见,因此永远没有理由不升级到您当前分支的最新版本。分支通过两个由点分隔的数字进行编号,例如“1.6”。一个完整的版本包含一个或两个子版本号,表示修复的级别。例如,版本 1.5.14 是在版本 1.5.0 发布后,1.5 分支上的第 14 个修复版本。它包含了 126 个单个 bug 修复,24 个文档更新,以及 75 个其他回溯补丁,其中大部分是为了修复上述 126 个 bug。为了保证同一分支内的升级总是安全的,稳定分支中的现有功能永远不会被修改或删除。HAProxy 可从多个来源获得,发布节奏不同:- 官方社区网站:https://haproxy.cn/:该网站提供最新开发版本、所有稳定版本以及每个分支的夜间快照的源代码。发布周期不快,稳定版本之间或开发快照之间有几个月的时间。非常旧的版本仍然在那里得到支持。所有内容都仅以源代码形式提供,因此任何来自那里都需要重新构建和/或重新打包;- 许多操作系统,如 Linux 发行版和 BSD 端口。这些系统通常提供长期维护的版本,这些版本不一定包含官方版本的所有修复,但至少包含关键修复。对于大多数不寻求高级配置、只想轻松更新的用户来说,这通常是一个不错的选择;- 来自 http://www.haproxy.com/ 的商业版本:这些是为各种操作系统构建或作为设备提供的支持的专业软件包,基于最新稳定版本,并包含从下一个版本回溯的许多功能,这些功能有很强的需求。对于寻求最新功能、稳定分支的可靠性、最快的 bug 修复响应时间,或者仅仅是开源产品之上的支持合同的用户来说,这是最佳选择;为了确保您使用的版本是您所在分支的最新版本,您需要按以下方式进行操作:- 验证您正在运行的 HAProxy 可执行文件:有些系统默认包含它,管理员会在系统的其他地方安装自己的版本,因此验证启动脚本中使用的是哪个版本很重要;- 确定您的 HAProxy 版本来自哪个来源。通常,键入“haproxy -v”就足够了。开发版本会像这样显示,分支号后带有“dev”字样:HA-Proxy version 1.6-dev3-385ecc-68 2015/08/18 稳定版本以及操作系统供应商提供的未修改的稳定版本会像这样显示:HA-Proxy version 1.5.14 2015/07/02 夜间快照版本会像这样显示,版本号后有一个十六进制序列,日期是快照日期而不是发布日期:HA-Proxy version 1.5.14-e4766ba 2015/07/29 任何其他格式可能表明是具有自己补丁集的特定于系统的软件包。例如,HAProxy Enterprise 版本将以以下格式显示(<branch>-<latest commit>-<revision>):HA-Proxy version 1.5.0-994126-357 2015/07/02 - 对于特定于系统的软件包,您必须咨询您的供应商的软件包存储库或更新系统,以确保您的系统仍然受支持,并且为您的分支仍然提供修复。对于来自 haproxy.org 的社区版本,只需访问网站,验证您分支的状态,并将最新版本与您的进行比较,看看您是否在最新版本上。如果没有,您可以升级。如果您的分支不再维护,那么您肯定已经落后很多了,将不得不考虑升级到更近的分支(升级时请仔细阅读 README)。HAProxy 的更新将取决于它来自的来源。通常,它遵循系统供应商升级软件包的方式。如果它是从源代码获取的,请在解压源代码后阅读源代码目录中的 README 文件,并按照您操作系统的说明进行操作。
HAProxy 与下面列出的一些产品集成得相当好,因此即使它们不直接与 HAProxy 相关,也在此处提及。
Apache 是事实上的标准 HTTP 服务器。它是一个非常完整且模块化的项目,支持文件服务和动态内容。它可以作为某些应用程序服务器的前端。它甚至可以代理请求和缓存响应。在所有这些用例中,通常都需要一个前端负载均衡器。Apache 可以以多种模式工作,有些模式比其他模式更重。某些模块仍然需要更重的预派生模型,这将阻止 Apache 在大量连接下很好地扩展。在这种情况下,HAProxy 可以通过将每个服务器的连接数强制设置为安全值来提供巨大的帮助,并显著加快服务器速度并保留其资源,以便应用程序更好地利用这些资源。Apache 可以使用“mod_rpaf”扩展从 X-Forwarded-For 标头中提取客户端的地址。当在 HAProxy 的配置中指定“option forwardfor”时,HAProxy 会自动填充此标头。当暴露给互联网时,HAProxy 还可以为 Apache 提供良好的保护,使其能够更好地抵御各种类型的 DoS 攻击。
NGINX 是事实上的第二个标准 HTTP 服务器。与 Apache 一样,它涵盖了广泛的功能。NGINX 的模型与 HAProxy 类似,因此它在处理数万个并发连接方面没有任何问题。当用作某些应用程序(例如使用包含的 PHP FPM)的网关时,通常可以设置一些前端连接限制来减少 PHP 应用程序的负载。HAProxy 在这里显然有用,既可以作为常规负载均衡器,也可以作为流量调节器,通过解压来加速 PHP。此外,由于这两个产品都由于其事件驱动的架构而占用很少的 CPU,因此通常可以轻松地将它们都安装在同一系统上。NGINX 支持 HAProxy 的 PROXY 协议,因此 HAProxy 可以轻松地将客户端的连接信息传递给 NGINX,以便应用程序获得所有相关信息。一些基准测试还表明,对于大型静态文件服务,在 NGINX 前面使用 HAProxy 实现一致性哈希可能是有益的,通过优化操作系统的缓存命中率,缓存命中率基本上是服务器节点数的倍数。
Varnish 是一个智能的缓存反向代理,可能最好被描述为一个 Web 应用程序加速器。Varnish 不实现 SSL/TLS,并希望将其所有 CPU 周期专注于它最擅长的事情。Varnish 也实现了 HAProxy 的 PROXY 协议,因此 HAProxy 可以非常容易地部署在 Varnish 前端,作为 SSL 卸载器和负载均衡器,并向其传递所有相关的客户端信息。此外,当服务器提供了压缩对象时,Varnish 天然支持从缓存中解压缩,但它本身不进行压缩。当后端服务器未实现压缩时,HAProxy 可以用于压缩传出的数据,尽管在负载均衡器上进行压缩通常不是一个好主意,除非流量很低。当在多个节点上构建大型缓存集群时,HAProxy 可以利用一致性 URL 哈希来智能地将负载分配到缓存节点,并避免缓存重复,从而使总缓存大小等于所有缓存节点的总和。
Linux Virtual Server (LVS 或 IPVS) 是包含在 Linux 内核中的第 4 层负载均衡器。它在数据包级别工作,处理 TCP 和 UDP。在大多数情况下,它更像是补充而不是替代,因为它根本没有第 7 层知识。Pound 是另一个知名的负载均衡器。它比 HAProxy 简单得多,功能也少得多,但对于许多非常基本的设置,两者都可以使用。其作者一直将代码审计性放在首位,并希望保持功能集较低。其基于线程的架构在高连接数下扩展性较差,但它是一个好产品。Pen 是一个相当轻量级的负载均衡器。它支持 SSL,使用固定大小的客户端 IP 地址表来维护持久性。它支持面向数据包的模式,允许它在一定程度上支持直接服务器返回和 UDP。它适用于小负载(持久性表只有 2048 个条目)。NGINX 在一定程度上也可以进行负载均衡,尽管这显然不是它的主要功能。生产流量用于检测服务器故障,负载均衡算法更有限,并且粘性非常有限。但在某些已经部署它的简单部署场景中,它可能是有意义的。好处是,由于它与 HAProxy 集成得非常好,当达到其限制时,稍后添加 HAProxy 没有问题。Varnish 也对后端服务器进行负载均衡,并支持真实的健康检查。它不支持粘性,因此与 NGINX 一样,只要不需要粘性,一开始就足够了。同样,由于 HAProxy 和 Varnish 集成得非常好,可以轻松地将它稍后添加到组合中以补充功能集。