在开发高负载的网络应用程序时,需要进行负载平衡。
流行的L7平衡工具是Nginx。 它允许您缓存答案,选择不同的策略,甚至在LUA上编写脚本。
尽管拥有Nginx的所有魅力,但如果:
- 无需使用HTTP。
- 您需要将最大流量从网络中挤出。
- 无需缓存任何内容-平衡器具有干净的动态API服务器。
可能会出现问题:您为什么需要Nginx? 为什么要在L7上花费资源平衡,简单地转发SYN数据包会更容易吗? (L4直接路由)。
第4层平衡或古代平衡
流行的数据包转发工具是IPVS。 他通过隧道和直接路由执行平衡任务。
在第一种情况下,为每个连接建立一个TCP通道,来自用户的数据包先到达平衡器,再到达小仆,然后以相反的顺序。

在这种方案中,主要问题是显而易见的:在相反的方向上,数据首先到达平衡器,然后到达用户(Nginx的工作方式相同)。 鉴于通常会向用户发送更多数据这一事实,因此执行了不必要的工作,这种行为会导致性能下降。
这样的缺点被剥夺了(但赋予了新的)一种称为直接路由的平衡方法。 从示意图上看,它看起来像这样:

对于直接路由,返回数据包将绕过平衡器直接发送给用户。 显然,平衡器资源和网络都可以节省。 通过节省网络资源,并不能节省太多流量,因为通常的做法是将服务器连接到单独的网格,而不是考虑流量,但是即使通过平衡器进行传输也要浪费几毫秒的事实。
此方法施加了某些限制:
- 基础结构所在的数据中心应允许欺骗本地地址。 在上图中,每个奴才必须代表分配给平衡器的IP 10.10.0.1发送回数据包。
- 平衡器对小兵的状态一无所知。 因此,开箱即用的Least Conn和Least Time策略是不可行的。 在以下文章之一中,我将尝试实现它们并显示结果。
NFTable来了
几年前,Linux开始积极推广NFTables,以取代IPTables,ArpTables,EBTables和其他所有[az] {1,}表。 当我们在Adram中需要从网络中挤出每毫秒的答案时,我决定拔出检查器并进行搜索-也许ipTables学会了进行iphash转发,您可以加快它来平衡它。 然后我偶然发现了nftables,它不仅可以做到这一点,而且iptables甚至不能做到这一点。
经过几天的试用,我终于能够在测试实验室中通过NFTables获得直接路由和通道路由,并与nginx进行比较。
因此,测试实验室。 我们有5辆车:
- nft-router-执行连接客户端和AppServer子网任务的路由器。 上面有2个网卡:192.168.56.254-查看应用程序服务器的网络,192.168.97.254-查看客户端。 启用ip_forward并注册所有路由。
- nft-client:将从其追逐ab,ip 192.168.97.2的客户端
- nft-balancer:平衡器。 它具有两个IP:客户端从minion子网访问的192.168.56.4和192.168.13.1。
- nft-minion-a和nft-minion-b:minions ipy:192.168.56.2、192.168.56.3以及192.168.13.2和192.168.13.3(我尝试同时使用相同的网络和不同的网络来平衡)。 在测试中,我停下了一个奴才具有“外部”类型的事实-在子网192.168.56.0/24中
所有MTU 1500接口。
直接路由
平衡器上的NFTables设置:
table ip raw { chain input { type filter hook prerouting priority -300; policy accept; tcp dport http ip daddr set jhash tcp sport mod 2 map { 0: 192.168.56.2, 1: 192.168.56.3 } } }
在钩上创建的原始链的优先级为-300。
如果到达目标地址为http的数据包,则根据源端口(从一台计算机进行测试,您实际上需要ip saddr),选择56.2或56.3并将其设置为数据包中的目标地址,然后沿路由进一步发送。 粗略地说,对于偶数端口56.2,对于奇数端口,分别为56.3(实际上,不是,因为对于偶数/奇数哈希,但是更容易理解这一点)。 设置目标IP后,数据包将返回网络。 没有NAT发生,该软件包将附带客户端的源IP,而不是平衡器,这对于直接路由很重要。
Minion NFT设置:
table ip raw { chain output { type filter hook output priority -300; policy accept; tcp sport http ip saddr set 192.168.56.4 } }
创建的原始输出挂钩的优先级为-300(优先级在这里非常重要,在更高级别上,必要的修改将不适用于回复数据包)。
来自http端口的所有传出流量均由56.4(IP平衡器)签名,并绕过平衡器直接发送到客户端。
为了检查一切是否正常,我将客户端带到另一个网络,并使其通过了路由器。
我还禁用了arp_filter,rp_filter(以便进行欺骗)并在平衡器和路由器上启用了ip_forward。
对于长凳,在NFT的情况下,通过每个奴才上的unix套接字使用Nginx + php7.2-FPM。 平衡器上没有任何东西。
在Nginx的情况下,我们使用了:平衡器上的nginx和小兵上的TCP上的php7.2-FPM。 结果,我没有在平衡器后面平衡Web服务器,而是立即平衡FPM(这对nginx来说更诚实,与现实生活更加一致)。
对于NFT,仅使用哈希策略(表中为
nft dr ),对于nginx:哈希(
ngx eq )和最小conn(
ngx lc )
已经完成了一些测试。
- 小快速脚本(小) 。
<?php system('hostname');
- 具有随机延迟(rand)的脚本。
<?php usleep(mt_rand(100000,200000)); echo "ok";
- 发送大量数据(大小)的脚本。
<?php $size=$_GET['size']; $file='/tmp/'.$size; if (!file_exists($file)) { $dummy=""; exec ("dd if=/dev/urandom of=$file bs=$size count=1 2>&1",$dummy); } fpassthru (fopen($file,'rb'));
使用了以下尺寸:
512.1440.1460.1480.1500.2048.65535.655350字节。
在测试之前,我预热了每个奴才上的静态文件。
每次测试均进行了3次AB测试:
最初,我计划提供测试时间,毫秒和其他时间,因此我决定使用RPS-它们具有代表性,并且与时间指标相关。
得到了以下结果:
大小测试-列-给定数据的大小。

如您所见,nft直接路由获得了巨大的成功。
我指望的是与以太网帧大小有关的略有不同的结果,但是没有发现相关性。 也许512机体无法容纳1500 MTU,但我怀疑,小测试将是指示性的。
我注意到,在大容量(650k)上,nginx减少了分离。 也许这与缓冲区和TCP Windows大小有关。
兰德测试的结果。 显示最小conn如何在不同的奴才上以不同的脚本执行速度处理条件。

令人惊讶的是,nginx哈希算法比最小conn的运行速度更快,并且只有在最后一次传递中,最小conn才稍微领先一步,这并不假装意义重大。
由于一次要离开100个线程,并且开始时的FPM-ok负载大约为10,因此通过的次数有很大不同。到第三次通过,他们有时间习惯-这表明突发策略的适用性。
NFT预期会失去这项测试。 在这种情况下,Nginx很好地优化了与FPM的交互。
小测试

nft在RPS上微弱地获胜,至少conn再次是局外人。
顺便说一下,在该测试中可以看到发出了400-500RPS的数据,尽管在发送512字节的测试中它是1500的-看来系统吃掉了这千字节。
结论
在优化均匀负载的情况下,NFT表现良好:当提供大量数据,确定应用程序的运行时间并且群集的资源足以处理传入流而不会陷入混乱时。
在每个请求的负载混乱并且无法将服务器负载与哈希划分的原始剩余部分平均平衡的情况下,NFT将丢失。