HAProxy 3.4.4
1. 快速 HTTP 提示
HAProxy 3.4 入门、配置与管理三套完整手册的简体中文译本
本文涵盖上述指定版本中实现的配置语言。 本文不提供任何提示、示例或建议。 如需此类文档,请参阅参考手册或架构手册。 编号章节在 HAProxy 扁平侧边栏中按顺序排列,支持直接导航。
当 HAProxy 以 HTTP 模式运行时,请求和响应均会被完整分析并索引,因此可基于内容中发现的几乎任何信息构建匹配条件。
然而,理解 HTTP 请求和响应的构成方式,以及 HAProxy 如何对其进行解析,至关重要。掌握这些原理后,编写正确的规则以及排查现有配置将变得更加容易。
首先,HTTP 由一系列 RFC 标准化,HAProxy 尽可能严格地遵循这些标准:
- RFC 9110:HTTP 语义(解释协议元素的含义)
- RFC 9111:HTTP 缓存(解释 HTTP 缓存应遵循的规则)
- RFC 9112:HTTP/1.1(表示形式、互操作性规则、安全性)
- RFC 9113:HTTP/2(表示形式、互操作性规则、安全性)
- RFC 9114:HTTP/3(表示形式、互操作性规则、安全性)
此外,RFC 8999 至 9002 规定了 HTTP/3 协议所使用的 QUIC 传输层。
1.1. HTTP 事务模型
HTTP 协议是基于事务的。这意味着每个请求只会对应一个且仅一个响应。最初,在协议版本 1.0 时,每个连接仅支持一个请求:客户端与服务器建立 TCP 连接,客户端通过该连接发送请求,服务器返回响应,随后连接关闭。新的请求则需要建立新的连接:
在该模式下,通常称为“HTTP 关闭”模式,连接建立次数与 HTTP 事务次数相同。由于服务器在发送响应后关闭连接,客户端无需知晓内容长度,当连接关闭时即认为响应已完整。这也意味着,若因网络错误导致部分响应被截断,客户端可能误认为响应已完整,从而导致图像偶尔出现渲染不全的情况。
由于协议的事务性特征,可以对其进行优化,以避免在两个后续事务之间关闭连接。然而,在此模式下,服务器必须为每个响应标明内容长度,以免客户端无限期等待。为此,使用了一个特殊头:“Content-length”。此模式称为 “keep-alive” 模式,随 HTTP/1.1 一同引入(部分 HTTP/1.0 客户端也支持),在请求间复用的连接被称为“持久连接”:
其优势在于事务间的延迟更低,服务器端所需的处理能力更少,并且能够检测到响应被截断的情况。通常情况下,该模式比关闭模式更快,但并非总是如此,因为部分客户端通常会将其并发连接数限制在较低值,这在网络连接较差时难以弥补。此外,部分服务器需长时间保持连接以等待可能到来的新请求,可能导致因连接数量过多而产生较高的内存使用,过快关闭连接可能中断恰好在连接关闭瞬间到达的请求。
在此模式下,响应大小需事先知晓,因此对于动态生成或压缩的内容,这并不总是可行。为此,实现了另一种模式,即“分块传输模式”,在该模式中,发送方不再一次性声明整个响应的大小,而是仅通告其当前缓冲区中已有的下一段 “chunk” 响应的大小,并可在任意时刻以大小为零的分块终止传输。在此模式下,不使用 Content-Length 头。
通信机制的另一项改进是流水线模式。该模式仍使用持久连接,但客户端在未收到首个响应时即可发送第二个请求。此模式在获取构成页面的大量图像时尤为有用:
这显然能显著提升性能,因为后续请求之间的网络延迟被消除。许多 HTTP 客户端不正确地支持流水线化,因为在 HTTP 中无法将响应与对应的请求关联。因此,服务器必须以与接收请求完全相同的顺序进行回复。实际上,经过多个客户端多次尝试部署后,由于在某些服务器上可靠性不足,该机制已被完全弃用。但服务器必须支持此功能。
下一个改进是多路复用模式,如 HTTP/2 和 HTTP/3 中所实现。在此模式下,多个事务(即请求-响应对)可在单个连接上并行传输,且各自以独立速度推进,互不干扰。采用多路复用协议时,引入了 “stream” 的新概念,用于表示在同一连接上发生的并行通信。每个流通常为特定连接分配唯一的标识符,两端均使用该标识符确定数据的交付位置。客户端在同一个连接上同时开启多个流(最多可达 100 个,有时更多)十分常见,由服务器负责排序,并根据响应的可用性以任意顺序进行响应。多路复用模式的主要优势在于显著减少了往返次数,从而加快了高延迟网络上的页面加载速度。在使用大量图片的网站上,这一特性有时可明显观察到所有图片几乎同时加载。
这些协议通过采用某些机制压缩头字段,以减少网络传输的字节数,因此在缺乏适当工具的情况下,它们无法像 HTTP/1 那样通过人工方式实际操作或肉眼直接阅读。出于这一原因,尽管协议版本更新,文献(包括本文)中仍继续使用 HTTP/1 语法来表示各种 HTTP 消息示例。
HTTP/2 存在一些设计上的局限性,例如数据包丢失会同时影响所有流,若客户端获取对象耗时过长(例如需要将其存储到磁盘),可能会导致其自身获取速度变慢,并在此期间无法访问其后方待处理的数据。这被称为“队首阻塞”或“HoL 阻塞”,有时也简称为 “HoL”。
HTTP/3 基于 QUIC 实现,而 QUIC 本身基于 UDP 实现。QUIC 通过独立处理流的方式,在传输层解决了队首阻塞问题。当发生丢包时,受影响的流不会影响其他流,所有流均可并行访问。QUIC 还提供连接迁移支持,但当前 HAProxy 尚不支持该功能。
默认情况下,HAProxy 以持久连接模式运行:对于每个连接,它在处理完每个请求和响应后,会在响应结束与新请求开始之间保持连接空闲。当从客户端接收 HTTP/2 连接时,它会并行处理所有请求,并保持连接空闲,等待新的请求,其行为与持久连接的 HTTP 连接相同。
HAProxy 本质上支持三种连接模式:
-
keep alive:所有请求和响应均被处理,客户端面向和服务器面向的连接将保持活跃,以供后续请求使用。这是默认行为,适用于现代 Web 及现代协议(HTTP/2 和 HTTP/3)。
-
服务端关闭:在响应发送完成后,面向服务器的连接被关闭。
-
close:在双方响应结束后,主动关闭连接。
此外,默认情况下,面向服务器的连接可被任意客户端的任意请求复用,这是由 HTTP 协议规范所要求的,因此如需使用特定客户端的信息(例如客户端的源地址等),必须随每个请求一并传递。当使用 HTTP/2 与服务器通信时,默认情况下 HAProxy 会将该连接专用于同一客户端,以避免客户端之间出现队头阻塞风险。
1.2. 术语
在 HAProxy 中,术语随着时代演进而有所演变,以适应 HTTP 协议及其使用方式的发展。最初,连接、会话、流或事务之间并无显著区别,但随着时间推移,这些术语逐渐明确,以更贴近现代 HTTP 协议的实际形态,尽管部分术语仍保留在配置或命令行界面中,以维持历史兼容性。
以下是适用于当前 HAProxy 版本的一些定义:
-
连接:连接是客户端或服务器等远程代理与 HAProxy 之间在最低层级上建立的单个双向通信通道。通常对应于一对 IP 地址和端口之间建立的 TCP 套接字。在面向客户端的一侧,当客户端连接到 HAProxy 时,连接是首先被实例化的实体,作用于连接层级的规则是最早生效的规则。
-
会话:会话为连接附加了一些上下文信息,包括与传输层相关的信息(例如 TLS 密钥等)或变量。该术语长期以来在 HAProxy 中用于表示两端之间的端到端 HTTP/1.0 通信,因此尽管如今其含义已演变为流,但某些 CLI 命令或统计信息的名称中仍保留该术语,不过帮助消息和描述力求使其含义清晰明确。在网络层术语中(例如操作系统中的 TCP 会话,或防火墙两侧的 TCP 会话)或非 HTTP 的用户级应用中(例如 Telnet 会话或 SSH 会话),该术语仍然适用。不得将其与“应用会话”混淆,后者用于在 Cookie 中存储完整的用户上下文,并要求始终将请求发送至同一服务器。
-
流:流在应用层精确对应于端到端的双向通信,可在其上应用分析和转换。在 HTTP 中,流包含一个请求及其关联的响应,由请求到达时创建,并在响应传输结束时终止。在此上下文中,此类流与多路复用协议的流之间存在一对一关系。在 TCP 通信中,每个连接对应单一的流。
-
transaction:事务仅指一个请求及其关联的响应。该术语在流出现之前曾与会话一同使用,但如今事务与流之间存在一对一关系。其本质体现在变量作用域 “txn” 中,该作用域贯穿整个事务,因此也覆盖整个流。
-
request:指从客户端流向服务器的流量。主要用于 HTTP,表示操作执行的位置。该术语在 TCP 操作中也存在,用于表示数据处理的位置。请求通常以流量或活动单位的形式出现在计数器中。请求不一定意味着存在响应(例如由于错误),但由于没有请求就不会有自发响应,因此请求仍是衡量整体活动的合理指标。在 TCP 中,请求的数量与连接数量相等。
-
response:此选项指定从服务器流向客户端的流量,或在 HAProxy 自行生成响应时从 HAProxy 流向客户端的流量(例如 HTTP 重定向)。
-
service:通常表示 HAProxy 内部无需服务器参与的处理过程,例如统计页面、缓存或用于实现小型应用的 Lua 代码。服务通常会读取请求,执行某些操作,并生成响应。
1.3. HTTP 请求
首先,考虑以下 HTTP 请求:
Line Contents
number
1 GET /serv/login.php?lang=en&profile=2 HTTP/1.1
2 Host: www.mydomain.com
3 User-agent: my small browser
4 Accept: image/jpeg, image/gif
5 Accept: image/png1.3.1. 请求行
第 1 行为“请求行”。它始终由 3 个字段组成:
- 方法:GET
- URI:/serv/login.php?lang=en&profile=2
- 版本标签:HTTP/1.1
所有字段均以标准所称的 LWS(线性空白字符)分隔,通常为空格,但也可能为制表符,或换行符/回车符后跟空格/制表符。方法本身不得包含冒号(’:’),且仅限字母字符。这些多种组合使得 HAProxy 自行执行分割操作更为可取,而非由用户编写复杂或不准确的正则表达式。
URI 本身可以有多种形式:
- 相对 URI:
/serv/login.php?lang=en&profile=2
It is a complete URL without the host part. This is generally what is
received by servers, reverse proxies and transparent proxies.- “绝对 URI”,也称为 “URL”:
http://192.168.0.12:8080/serv/login.php?lang=en&profile=2
It is composed of a "scheme" (the protocol name followed by '://'), a host
name or address, optionally a colon (':') followed by a port number, then
a relative URI beginning at the first slash ('/') after the address part.
This is generally what proxies receive, but a server supporting HTTP/1.1
must accept this form too.-
星号(’*’):此形式仅在与 OPTIONS 方法关联时被接受,且不可中继。它用于查询下一跳的能力。
-
地址:端口组合:192.168.0.12:80。此配置与 CONNECT 方法配合使用,该方法用于通过 HTTP 代理建立 TCP 隧道,通常用于 HTTPS,有时也用于其他协议。
在相对 URI 中,识别出两个子部分。问号之前的部分称为 “path”,通常为服务器上静态资源的相对路径。问号之后的部分称为“查询字符串”,主要用于发送至动态脚本的 GET 请求,其具体格式高度依赖于所使用的语言、框架或应用程序。
HTTP/2 和 HTTP/3 不会在请求中传递版本信息,因此默认认为其版本与底层协议一致(即 “HTTP/2”)。此外,这些协议不会将请求行作为一个整体发送,而是将其拆分为若干字段,称为 “pseudo-headers”,其名称以冒号开头,HAProxy 会将其方便地重新组合为等效的请求行。因此,日志中记录的请求行在 HTTP/1.x 与 HTTP/2 或 HTTP/3 之间可能存在细微差异。
1.3.2. 请求头
头从第二行开始。头由行首的名称组成,名称后立即跟一个冒号(’:’)。传统上,冒号后会添加一个线性空白字符(LWS),但并非必须。随后是值。多个相同的头可以合并为一行,通过逗号分隔值,前提是必须保持顺序。这在 “Cookie:” 字段中较为常见。如果后续行以 LWS 开头,头可以跨多行。在 1.3 节的示例中,第 4 行和第 5 行共同为 “Accept:” 头定义了总共 3 个值。最后,根据规范,头开头或结尾的所有 LWS 均被忽略,不计入值中。
与常见误解相反,头名称不区分大小写,其值在引用其他头名称(如“Connection:”头)时同样不区分大小写。在 HTTP/2 和 HTTP/3 中,头名称始终以小写形式发送,这一点在启用调试模式运行时可以观察到。内部实现中,所有头名称均被规范化为小写,以确保 HTTP/1.x 与 HTTP/2 或 HTTP/3 使用完全相同的表示形式,并在另一端原样发送。这解释了为何以驼峰命名法输入的 HTTP/1.x 请求在接收时会以小写形式呈现。
头的结束由第一个空行指示。人们常说这是两个换行符,这并不准确,尽管两个换行符是空行的一种有效形式。
幸运的是,HAProxy 在处理头索引、值检查和计数时会自动处理所有复杂的组合,因此无需担心头的写法,但若应用程序执行了非寻常但合法的操作,不应将其归咎于存在缺陷。
请注意:
As suggested by RFC7231, HAProxy normalizes headers by replacing line breaks
in the middle of headers by LWS in order to join multi-line headers. This
is necessary for proper analysis and helps less capable HTTP parsers to work
correctly and not to be fooled by such complex constructs.1.4. HTTP 响应
HTTP 响应与 HTTP 请求非常相似。两者均称为 HTTP 消息。请考虑以下 HTTP 响应:
作为特例,HTTP 支持所谓的“信息性响应”,即状态码 1xx。这类消息的特殊之处在于,它们不携带响应的任何部分,仅用作一种信号,例如通知客户端继续发送其请求。对于状态码 100 的响应,所请求的信息将由后续非 100 状态码的响应消息携带。这意味着单个请求可能收到多个响应,且此机制仅在启用持久连接时有效(1xx 消息出现在 HTTP/1.1 中)。HAProxy 能够正确处理此类消息,可将其转发并跳过,仅处理下一个非 100 状态码的响应。因此,除非明确另行说明,否则这些消息既不会被记录,也不会被转换。状态码 101 的响应表示协议将在同一连接上发生变更,HAProxy 必须切换至隧道模式,如同发生了 CONNECT 请求一般。此时,Upgrade 头将包含关于连接所切换协议类型的附加信息。
1.4.1. 响应行
第 1 行是“响应行”。它始终由 3 个字段组成:
- 版本标签:HTTP/1.1
- 状态码:200
- 原因:OK
状态码始终为三位数字。第一位数字表示总体状态:
- 1xx = 信息性消息,应跳过(例如 100、101)
- 2xx = 成功,后续有内容(例如 200、206)
- 3xx = 成功,后续无内容(例如 302、304)
- 4xx = 客户端引起的错误(例如 401、403、404)
- 5xx = 服务器引起的错误(例如 500、502、503)
状态码大于 599 时,不得在通信中发出,尽管某些代理可能会在日志中生成此类状态码以报告其内部状态。有关所有此类状态码的详细含义,请参阅 RFC9110。HTTP/2 及以上版本不使用版本标签,而是使用 “:status” 伪头来报告状态码。
“reason” 字段仅作为提示,客户端不会解析该字段。该字段可包含任意内容,但通常建议遵循已确立的通用消息规范。其内容可由一个或多个单词组成,例如 “OK”、“Found” 或 “Authentication Required”。该字段在 HTTP/2 及以上版本中不存在,也不会在这些版本中发出。当从 HTTP/2 或更高版本返回的响应传输至 HTTP/1 客户端时,HAProxy 将生成与状态码匹配的通用原因字段。
HAProxy 可能自行发出以下状态码:
Code When / reason
200 access to stats page, and when replying to monitoring requests
301 when performing a redirection, depending on the configured code
302 when performing a redirection, depending on the configured code
303 when performing a redirection, depending on the configured code
307 when performing a redirection, depending on the configured code
308 when performing a redirection, depending on the configured code
400 for an invalid or too large request
401 when an authentication is required to perform the action (when
accessing the stats page)
403 when a request is forbidden by a "http-request deny" rule
404 when the requested resource could not be found
408 when the request timeout strikes before the request is complete
410 when the requested resource is no longer available and will not
be available again
413 when a HTTP/1.0 GET/HEAD/DELETE requests has a payload, also see
the "h1-accept-payload-with-any-method" option
500 when HAProxy encounters an unrecoverable internal error, such as a
memory allocation failure, which should never happen
501 when HAProxy is unable to satisfy a client request because of an
unsupported feature
502 when the server returns an empty, invalid or incomplete response, or
when an "http-response deny" rule blocks the response.
503 when no server was available to handle the request, or in response to
monitoring requests which match the "monitor fail" condition
504 when the response timeout strikes before the server responds上述 4xx 和 5xx 错误码可自定义(参见 “errorloc” 在 第 4.2 节 )。其他状态码可通过特定动作主动发出(例如,参见 “deny”、“return” 和 “redirect” 动作在 第 4.3 节 )。
1.4.2. 响应头
响应头的工作方式与请求头完全相同,因此 HAProxy 对两者使用相同的解析函数。详情请参见第 1.3.2 段。
来源与许可
文档取自 pig.center · 上游文档
- 版本
- 3.4.4
- 许可
- GPL-2.0-only
- 来源修订
1dff183d0ca5a430d10324c5bbc64f9853ed77ae5df299827b47b2f54e352f65- 译文修订
1dff183d0ca5a430d10324c5bbc64f9853ed77ae5df299827b47b2f54e352f65