NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

HAProxy 1.9 发布,支持端到端 HTTP/2,改进缓冲区和连接管理

  • 2018-12-22
  • 本文字数:6631 字

    阅读完需:约 22 分钟

HAProxy 1.9发布,支持端到端 HTTP/2,改进缓冲区和连接管理

HAProxy Technologies 正式宣布推出 HAProxy 1.9。新版本带来了基于原生 HTTP 表示(HTX)的端到端 HTTP/2 支持,并为未来的创新——如 HTTP/3(QUIC)——铺平了道路。它还带来了缓冲区和连接管理方面的改进,包括到后端的连接池、线程优化、Runtime API,等等。


HAProxy 是世界上速度最快、使用最为广泛的软件负载均衡器,于 2001 年 12 月首次发布。从那时起,负载均衡器格局发生了重大变化。17 年来,HAProxy 一直在演化和创新,现在终于迎来了 HAProxy 1.9。


新版本的重点是为我们以后能够继续提供一流的性能和交付尖端功能奠定基础。得益于 1.9 版本的核心改进,一些令人兴奋的功能包括 Layer 7 重试机制、回路断路、gRPC、新的数据平面 API,等等。


我们还发现需要更频繁地发布更新。在未来,HAProxy 将从一年发布一次改为一年两次。之前,主要版本会在每年的 11 月或 12 月份左右发布,但从现在开始,我们将每年发布两次。请注意,这次的版本向后兼容旧的配置。


接下来,我们将深入探讨新版本带来的改进。

缓冲区改进

HAProxy 已经支持到客户端的 HTTP/2。新版本的主要目标是支持端到端的 HTTP/2,包括到后端服务器的 HTTP/2。我们还希望能够支持未来版本的 HTTP,例如 HTTP/3(QUIC)。


我们的研发团队付出了巨大的努力来实现这一目标,其中一个非常重要的方面涉及 HAProxy 处理缓冲区的方式。


缓冲区是一个存储区域,分为两部分:输入数据和输出数据。缓冲区可以在任何地方开始和结束。在之前版本的 HAProxy 中,有 22 种可能的缓冲区,如下图所示:



上图显示了 1.9 版本之前的各种缓冲区及其分配方式。我们决定重写缓冲区处理逻辑并简化缓冲区分配。下图显示了最新的缓冲区变更:



最新的变更将缓冲区的数量减少到 7 个,只需维护一个版本的代码。


除了这些重构之外,缓冲区的标头(即描述缓冲区状态的附加字段)已从存储区域中分离出来。这意味着对于相同的数据不再强制要求使用单一的表示,多个参与者可以在不同的状态下使用相同的存储。这通常被用在底层的多路复用中,通过读取器和写入器对相同数据块附加别名来避免内存复制(也即“零复制”),从而提升了 HTTP/2 的性能。


实现这一点并非易事,但却带来了很多好处。它为更容易实现端到端 HTTP/2 铺平了道路。它还简化了其他一些东西,比如内部 API、错误消息处理和渲染统计页面。

连接管理

HAProxy 1.9 中的连接管理获得了一些重大改进。新的实现已从面向回调的模型转变为异步事件模型(包含了完成回调)。这种新设计将会非常有用,并减少连接层中可能出现的 bug 数量。


新设计的一些好处包括:较低的 send()延迟(几乎从不进行轮询)、更少的层间往返(更好的 I-cache 效率)、在上层内直接使用、消除代码重复,以及提供较低层的细粒度错误报告。它还为不同协议提供了重试失败连接的能力(例如,如果 ALPN 表明同时支持 HTTP/2 和 HTTP/1,那么在其中一个发生故障时,会自动切换到另一个)。


如果没有设置,http-reuse 指令的默认值为 safe。这意味着到后端服务器的会话的第一个请求始终通过自己的连接发送,后续请求可以重用其他现有的空闲连接。这是几年来一直推荐的方式,现在是时候将其变为默认设置。


此外,HAProxy 现在也提供了连接池。当前端连接消失后,HAProxy 和服务器之间的空闲连接并不会立即被关闭,它们在服务器端保持打开状态,以供其他请求使用。

原生 HTTP 表示(HTX)

在研究支持未来版本 HTTP 所需的路由时,我们决定对 HTTP 消息的内部处理机制进行重新设计。之前,HTTP 消息作为字节流进行传输,数据的分析和处理任务被混合在同一个阶段中进行。



HTTP 信息作为字节流被收集起来,并通过偏移量来操作它们。主结构包含了两个通道:请求/响应和 HTTP 事务。第一个通道对请求和响应消息进行缓冲,将它们视为字符串。HTTP 事务通道有两种状态:一个是响应/请求,另一个包含了标头的偏移量。


因为所有东西都是以偏移量的方式进行保存,所以添加、删除和重写 HTTP 数据就会变得很麻烦,需要不断移动到标头的末尾,在处理响应消息时甚至需要移动到 HTTP 正文的末尾。随着时间的推移,cookie、keep-alive、压缩和缓存都需要操作标头,这项任务因此变得十分昂贵。



新的设计(我们称之为 HTX)为 HTTP 协议创建了内部的原生表示。它提供了一系列强类型、精心设计的标头字段,支持间隙和无序。现在修改标头很简单,只需要将旧标头删除,并在末尾添加新标头即可。



这让针对 HTTP 协议的操作变得更加简单,我们可以维护端到端的 HTTP 传输和语义,并在将 HTTP/2 转换为 HTTP/1.1 或将 HTTP/1.1 转换为 HTTP/2 时获得更高的性能。它将处理和分析相分离,现在分析和格式化发生在连接层,而处理发生在应用层。



由于我们还在进行测试,所以默认情况下尚未启用 HTX。你可以在 defaults、frontend、backend、listen 部分添加下面的选项来启用它:


option http-use-htx
复制代码


在启用后,你就可以在后端服务器上使用 HTTP/2。将 alpn h2 添加到服务器配置行中(或者如果你希望让 HAProxy 与服务器协商协议,可以添加 alpn h2,http/1.1)。


server server1 192.168.1.16:443 ssl verify none  alpn h2
复制代码


这是一个端到端 HTTP/2 的完整示例(frontend+backend):


frontend fe_main    mode http    option  http-use-htx     bind *:80     bind *:443 ssl crt /etc/hapee-1.8/certs/www.example.com.pem alpn h2,http/1.1    default_backend be_main
backend be_main mode http option http-use-htx server server1 192.168.1.13:443 ssl verify none alpn h2
复制代码


HAProxy 1.9 还支持 proto h2 指令,这个指令让 HAProxy 可以使用没有 TLS 的 HTTP/2 与支持 HTTP/2 的后端(如 Varnish 和 H2O)通信。你可以使用以下服务器配置启用这项功能:


server server1 192.168.1.13:80 proto h2
复制代码

多线程改进

1.9 版本对线程进行了重大改进。这些改进使得 HAProxy 能够提供卓越的性能。为实现这一目标,我们对任务调度程序进行了重新设计。它现在的工作被分为三个级别:


  • 在所有线程之间共享的优先级感知级别;

  • 无锁、优先级感知级别;

  • 可用于 I/O 的已启动任务。


现在大多数调度可以在没有锁的情况下进行,可以更好地扩展。此外,我们对调度程序的等待队列进行了优化。它们现在大部分都是无锁的。内存分配器是无锁的,并使用了热对象线程缓存,从而带来更快的结构初始化。文件描述符事件缓存基本上也是无锁的,允许更快的并发 I/O 操作。最后,文件描述符的锁已更新,因此使用的频率较低。总的来说,HAProxy 1.9 的线程性能提升了差不多 60%。

缓存改进

我们在 HAProxy 1.8 中引入了小对象缓存(Small Object Cache)。当时,很多人希望获得这些功能,而我们也知道这只一个开始:代理层中的缓存。在内部,我们将其称为 favicon 缓存,因为它仅限用于缓存小于 tune.bufsize 的对象,默认为 16KB。此外,在第一个版本中,它只能缓存返回 HTTP 200 OK 响应码的对象。


而在 HAProxy 1.9 中,你可以缓存最大为 2GB 的对象,使用 max-object-size 进行设置。total-max-size 用于指定缓存的总大小,最大为 4095MB。HAProxy 现在可以缓存返回以下状态码的响应:204、404、405、414 和 501。

HTTP 103

HAProxy 现在支持 HTTP 103 状态码,也称为 Early Hints(RFC8297),它允许你在服务器做出响应之前发送一系列对象链接到客户端。这项特性仍处在早期采用阶段,不过 Early Hints 看起来可能会取代 HTTP/2 服务器推送。


因为以下的一些因素,Early Hints 会比服务器推送更好:


  • 服务器推送可以加速资源的传递,但仅限于服务器允许的资源。换句话说,它必须遵循同源策略,某些情况下会对 CDN 的使用造成影响。而 Early Hints 可以直接指向 CDN 托管的对象。

  • Early Hints 让浏览器可以使用对象的本地缓存。而服务器推送会要求将请求传输至源,无论客户端是否缓存了响应。


要启用 Early Hints,请在 HAProxy 配置文件中添加类似以下这样的内容:


http-request early-hint Link "</style.css>; rel=preload; as=style"http-request early-hint Link "</script.js>; rel=preload; as=script"
复制代码

Runtime API 改进

Runtime API 也得到了更新。首先是修改了 master/worker 模型,简化 worker 的交互,并获得更好的进程可观察性。master 现在有自己的套接字,可以直接用这个套接字与它进行通信。而且套接字可以管理与每个 worker 之间的通信,甚至是那些正在退出的 worker。


要使用这个新功能,需要使用-W 和-S 选项启动 HAProxy。


$ haproxy -W -S /var/run/haproxy-master-socket  -f /etc/haproxy/haproxy.cfg
复制代码


然后通过 master 套接字连接到 Runtime API,如下所示:



新的 show proc 命令显示了每个进程的正常运行时间。



新的 reload 命令重新加载 HAProxy,并加载了新的配置文件。它与向 master 进程发送 SIGUSR2 信号是完全一样的效果,只是可以在上传新配置文件后由外部程序触发。


命令可以通过 master 套接字发给 worker,在命令前面加上 @符号,加上 worker 的编号。以下是向第一个 worker 发出 show info 命令的示例:



我们还添加了有效载荷支持,可以使用 Runtime API 插入多行文本。在更新 map 文件时这会非常有用。目前还不支持通过 Runtime API 更新 TLS 证书,但 HAProxy 2.0 有可能会支持!


要使用有效载荷更新 map 文件,你需要先 map 的 ID,然后使用 add map 命令添加新行,行间用\n 分隔:


$ echo "show map" | socat /var/run/haproxy.sock stdio# id (file) description-1 (/etc/haproxy/maps/hosts.map) pattern loaded from file '/etc/haproxy/maps/hosts.map' used by map at file '/etc/hapee-1.8/hapee-lb.cfg' line 94
$ echo -e "add map #-1 <<\ntest1 be_one\ntest2 be_two\n" | socat /var/run/haproxy.sock stdio
$ echo "show map #-1" | socat /var/run/haproxy.sock stdio0x217aa80 test1 be_one0x2190010 test2 be_two
复制代码


你也可以附加文件内容,比如:


$ echo -e "add map #-1 <<\n$(cat data.map)\n" | socat /var/run/haproxy.sock stdio
复制代码


Runtime API 中还新增了一个新的 show activity 命令。它可用于显示每个线程被系统抢占的总 CPU 时间,以及所有任务所经历的平均处理延迟。



同样,如果在 global 中配置启用或通过 Runtime API 启用了 profiling,那么就可以在日志中看到 CPU 时间和延迟。要在 global 中启用 profiling,你需要添加:


global    profiling.tasks on
复制代码


通过 Runtime API 启用:


$ echo "set profiling tasks on" |socat /var/run/haproxy.sock -
复制代码


验证是否已启用:


$ echo "show profiling" |socat /var/run/haproxy.sock -
复制代码

服务器队列优先级控制

HAProxy 1.9 允许你对队列中的连接进行优先级排序。例如,你可以让 JavaScript 或 CSS 文件优先于图像传给客户端。或者,你可以用它改善高级客户的加载时间。另外,可以用它给机器人安排较低的优先级。


通过添加 http-request set-priority-class 指令为 JS 或 CSS 文件设置更高的服务器队列优先级。为了避免由连续高优先级请求导致的饥饿等待,可以通过 set-priority-offset 指令为某些请求设置等待时间上限。将这项功能与 ACL 规则结合使用,你就可以灵活地决定何时以及如何安排连接的优先级。


acl is_jscss path_end .js .cssacl is_image path_end .png .jpg .jpeghttp-request set-priority-class 1 if is_jsscsshttp-request set-priority-class 10 if is_imagehttp-request set-priority-class 100 if !is_jscss !is_image
复制代码


数字越小,优先级越高。因此,在这里,JavaScript 和 CSS 文件被赋予最高优先级,其次是图像,然后是其他资源。

随机负载均衡算法

我们添加了一个新的随机负载均衡算法。这个算法会选择一个随机数作为一致哈希函数的键。在这个模式下,会用到服务器权重。动态权重更改会立即生效,新添加的服务器也会立即生效。随机负载均衡算法对于大型服务器机群或经常需要添加和删除服务器的场景来说非常有用。使用多个负载均衡器可以降低所有流量被重定向到同一服务器的风险。


hash-balance-factor 指令通过保持分配给服务器的负载接近于平均值来进一步提高负载均衡的公平性,当服务器的响应时间高度可变时,这会非常有用。


要启用随机负载均衡算法,请在 backend 中将 balance 设置为 random。


balance random
复制代码

云原生日志

HAProxy 已经具备了将日志写到 syslog 服务器的能力。但是,在使用 Docker 的微服务架构中,将 syslog 安装到容器中是一种反模式。用户要求使用其他方法来发送日志。我们已经从用户那里收到了相当多这类请求,所以花了一些时间选择了一种最佳的实现方式——非阻塞——我们很高兴已经找到了解决方案!


HAProxy 1.9 提供了三种发送日志的新方法:将它们发送到文件描述符、stdout 或 stderr。可以使用标准的 log 语句添加这些新方法。


要将日志记录到 stdout,请使用 stdout 参数:


log stdout local0
复制代码


对于 stderr 也类似。另一种方法是将日志发送到文件描述符,如下所示:


log fd@1 local0
复制代码


fd@1 参数是 stdout 的别名,fd@2 是 stderr 的别名。它们还有两种新的日志格式:raw 和 short。


log stdout  format raw  local0
复制代码

新的 Fetch

HAProxy 中的 Fetch 提供了来自内部状态或 L4、L5、L6 和 L7 的信息。新版本新增了 date_us、cpu_calls、lat_ns_avg 等 Fetch。

新的转换器

转换器可用于在 HAProxy 中转换数据,通常用在 Fetch 之后。新版本新增了 strcmp、concat、length、crc32c 等转换器。


杂项改进


新版本的 HAProxy 还添加了其他各种改进,包括:


  • 新的 stick-table 计数器 gpc1 和 gpc_rate。

  • resolvers 部分现在支持 resolv.conf。

  • busy-polling——在启用了频率缩放或支持深度空闲状态的机器上可将请求处理延迟减少 30-100 微秒。

  • HAProxy 的 Lua 引擎进行了以下更新:

  • Server 类可更改服务器的 maxconn 值。

  • TXN 类可调整服务器连接队列优先级。

  • 新类 StickTable 允许通过键来访问和转储 stick-table 的内容。

回归测试套件

Varnish 附带了一个叫作 varnishtest(https://varnish-cache.org/docs/trunk/reference/varnishtest.html)的工具,用于对整个 Varnish 代码库进行回归测试。我们发现它也是用来测试 HAProxy 特定用例的完美工具。我们与 Varnish 团队合作,为 varnishtest 提供补丁,对其进行扩展,并用它来测试 HAProxy。


我们还开始创建和发布可在用户环境中运行的测试代码。如果你了解 HAProxy,编写测试代码就很容易。所以,如果你有兴趣为 HAProxy 做贡献但不知道从哪里开始,你可以尝试先拉取 HAProxy 的代码,然后自己编写测试!


要使用回归测试套件,需要安装 varnishtest,它已经包含在 Varnish 软件包中。安装完成后,你需要创建一个测试 vtc 文件。这是一个示例:


varnishtest "Stick Table: Crash when accessing unknown key."feature ignore_unknown_macro
server s0 { rxreq txresp} -start
haproxy h0 -conf { defaults timeout connect 5000ms timeout client 50000ms timeout server 50000ms
frontend test mode http bind "fd@${fe1}" stick-table type ip size 1m expire 1h store gpc0 http-request deny if { src,table_trackers(test) eq 1 } http-request deny if { src,in_table(test) } http-request deny deny_status 200} -start
client c0 -connect ${h0_fe1_sock} { txreq -url "/" rxresp expect resp.status == 200} -run
复制代码


要运行它,需要将 HAPROXY_PROGRAM 环境变量设置为你要测试的二进制文件的路径。然后调用 varnishtest。


export HAPROXY_PROGRAM=$PWD/haproxy varnishtest /home/user/haproxy/reg-tests/stick-table/b00000.vtc#    top  TEST /home/user/haproxy/reg-tests/stick-table/b00000.vtc passed (0.112)
复制代码

HAProxy 2.0 预览

以下是将在 HAProxy 2.0 中带来的功能,计划于 2019 年 5 月发布:


  • HAProxy 数据平面 API;

  • gRPC;

  • L7 重试;

  • FastCGI 集成;

  • 回路断路;

  • 将 TLS 证书与私钥分离;

  • 使用 Runtime API 更新 TLS 证书和私钥。

结论

在开源社区和 HAProxy Technologies 的大力支持下,HAProxy 始终处于性能和创新的最前沿。我们很高兴为你带来 1.9 版本!它开启了一个新的篇章,你将看到更频繁的版本更新频率。它将支持端到端 HTTP/2,改进了缓冲区和连接管理,更新了 Runtime API 和小对象缓存,提供了新的随机负载均衡算法,甚至通过 Runtime API 和新的 Fetch 获得更好的可观察性。


英文原文:https://www.haproxy.com/blog/haproxy-1-9-has-arrived/


2018-12-22 13:313633
用户头像

发布了 731 篇内容, 共 434.4 次阅读, 收获喜欢 1997 次。

关注

评论 1 条评论

发布
暂无评论
发现更多内容

RocketMQ高性能设计之数据存储设计

急需上岸的小谢

8月月更

C++学习------cfenv头文件的作用与源码分析

桑榆

签约计划第三季

记一次 ClickHouse 性能测试

劼哥stone

SpringBoot 实战:国际化组件 MessageSource 与 Nacos 组合实现动态配置能力

看山

源码 nacos springboot MessageSources

极狐 GitLab 冷知识:使用 git push 创建 Merge Request

郭旭东

极狐GitLab JIHULAB 101

【ZK简明教程】(1)零知识证明的背景和系统结构

比特之心

区块链 零知识证明 密码学 签约计划第三季

[极致用户体验] 你的 Link Button 能让用户选择新页面打开吗?

HullQin

CSS JavaScript html 前端 8月月更

极狐 GitLab 冷知识:如何美化 issue 内容

郭旭东

极狐GitLab JIHULAB 101

数据库知识点总结

乌龟哥哥

8月月更

前端食堂技术周刊第 48 期:ESLint 新配置系统、Vue3 中文文档正式上线、Astro 1.0、小程序新渲染引擎

童欧巴

在线文字图标logo文章封面图生成工具

入门小站

工具

开源一夏|OpenHarmony跳转拨号界面

坚果

开源 OpenHarmony 8月月更

11 个需要避免的 React 错误用法

pingan8787

JavaScript typescript React

面试突击74:properties和yml有什么区别?

王磊

Java 常见面试题

Kafka基础知识

阿泽🧸

kafka 8月月更

开源一夏 | 见微知著,带你认认数据分析的大门,站在门口感受一下预测的魅力

迷彩

开源 数据分析 预测模型 签约计划第三季 8月月更

什么是研发效能?研发效能定义及核心价值

laofo

DevOps cicd 研发效能 持续交付 工程效率

对 Service Mesh 望而却步?可能都没理解这一点

baiyutang

架构 微服务 签约计划第三季 servicemes

云原生(十七) | Kubernetes篇之深入了解Deployment

Lansonli

云原生 8月月更

企业架构是当代的屠龙之术吗?

涛哥 数字产品和业务架构

企业架构

在线XML转Excel工具

入门小站

工具

头脑风暴:组合总和 Ⅳ

HelloWorld杰少

8月月更

三种插件开发模式,带你玩废tinymce

Five

tinymce 签约计划第三季 8月月更

阿里云解决方案架构师张平:云原生数字化安全生产的体系建设

阿里巴巴云原生

阿里云 云原生 安全 数字化

深入浅出sychronized与Lock的实现原理

清风

后端 原理 并发 lock sychronized

极狐 Git Lab 冷知识:妙用 Badge 徽章

郭旭东

极狐GitLab JIHULAB 101

急如闪电快如风,彩虹女神跃长空,Go语言高性能Web框架Iris项目实战-初始化项目ep00

刘悦的技术博客

Go golang 框架 go语言 Go 语言

IPv6报文头深度解析

穿过生命散发芬芳

ipv6 8月月更

《Effective Java》第54条:返回零长度的数组或者集合,而不是null

okokabcd

Java

Linux部署hadoop2.7.7集群

程序员欣宸

hadoop 8月月更

最多可省19%!阿里云第七代云服务器ECS中国大陆地域调价通知

阿里云弹性计算

云计算 阿里云 ECS 企业创新

HAProxy 1.9发布,支持端到端 HTTP/2,改进缓冲区和连接管理_软件工程_Daniel Corbett_InfoQ精选文章