从网络底层原理来告诉你为什么LVS比Nginx和HAproxy更高效

问题背景:项目要一个静态资源服务集群做负载均衡,我考虑的选型是用Nginx+静态缓存做七层负载,跟大佬沟通之后,他跟我说用LVS可以更加高效,理由是使用LVS的时候只有请求会过LVS,响应直接由静态资源服务器响应到客户端。听的我云里雾里,客户端的TCP请求得到的响应不是只能从LVS返回吗?难道还能由LVS接收请求,由静态资源服务直接返回?原来真的可以

请求:

客户端---->LVS---->静态资源服务器

响应:

静态资源服务器---->客户端

本文将对Nginx、HAproxy、LVS三种负载均衡服务进行对比。注意,只对它们充当四层负载时,其转发方式的对比。从转发的原理上来对比三种负载的区别,论证为什么LVS更加高效。

下面顺序为Nginx、HAproxy、LVS,分别先简单介绍各种负载均衡服务是什么,再简单说明它要进行四层负载需要怎么做,再以图文的形式论述其基于四层转发的原理,最后比对三者的区别。

一、Nginx

1、Nginx是什么?

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器。

Nginx除充当反向代理服务器外,它自己也是对标了Apache,可以充当一个web服务器。

其配置简单,功能强大。我自己的体验是,当你使用Nginx做七层负载的时候,它可以强大到修改你HTTP请求的任意内容、请求头、请求体;可以修改你HTTP请求响应的任意内容,响应头,响应体,还可以调整其负载时客户端请求时间的一些限制,请求时间…,向上游请求时的一些限制,响应时间等等。以下连接可以让你快速的了解到Nginx的各个模块、全局常量、表达式。

Nginx文档

对于Nginx的简单介绍就到此,如果您想要更加深入了解到Nginx又找不到很好的学习资源的话,看看

tengine开源的Nginx教程

2、Nginx配置四层代理需要怎么做

如果你了解过Nginx,那么你应该知道,Nginx做四层负载的时候使用的是ngx_streeam_core_modile模块,具体的配置方法可以从官方文档上面了解到

Nginx配置四层代理

以下举例一个简单的配置实例

worker_processes auto;

error_log /var/log/Nginx/error.log info;

events {
    worker_connections  1024;
}

stream {
    upstream backend {
        hash $remote_addr consistent;

        server backend1.example.com:12345 weight=5;
        server 127.0.0.1:12345            max_fails=3 fail_timeout=30s;
        server unix:/tmp/backend3;
    }

    upstream dns {
       server 192.168.0.1:53535;
       server dns.example.com:53;
    }

    server {
        listen 12345;
        proxy_connect_timeout 1s;
        proxy_timeout 3s;
        proxy_pass backend;
    }

    server {
        listen 127.0.0.1:53 udp reuseport;
        proxy_timeout 20s;
        proxy_pass dns;
    }

    server {
        listen [::1]:12345;
        proxy_pass unix:/tmp/stream.socket;
    }
}

3、Nginx如何实现四层负载均衡

如果你看完了Nginx运行的原理,那么你应该知道,Nginx所有的模块都是依赖Nginx主要的生命周期的(以下生命周期是根据上述文档链接了解得出的结论)

1、Nginx监听端口与ip地址,并等待请求接入
2、接入接入,Nginx内部创建了一个数据结构 ngx_connection_t,并传递给各个模块做一系列操作
3、模块根据请求做一些特殊处理。以ngx_streeam_core_modile为例,他用了几个阶段性的任务对请求做了处理
 	NGX_STREAM_POST_ACCEPT_PHASE = 0,
  	NGX_STREAM_PREACCESS_PHASE,
    NGX_STREAM_ACCESS_PHASE,
    NGX_STREAM_SSL_PHASE,
    NGX_STREAM_PREREAD_PHASE,
    NGX_STREAM_CONTENT_PHASE,
    NGX_STREAM_LOG_PHASE
4、当模块处理完请求之后呢,Nginx创建与上游服务的连接,并同样初始化一个与上游服务的 ngx_connection_t
5、通过读写事件转发了修改后的请求内容,并读取响应
6、响应内容同样经过模块的一波处理,这里就不再累述
7、客户端接受到了Nginx的响应

了解了Nginx的生命周期,我们大概可以整出这么一张图

在这里插入图片描述

从图中信息可以得出,当一个请求经过Nginx 的时候,在Nginx上至少会出现两个socket,一个是客户端到Nginx的,一个是Nginx到上游服务的,Nginx负责控制数据的处理与数据的转发,响应的处理,响应的转发。

好了记住这个结论,再来看看HAproxy

二、HAproxy

1、HAproxy是什么?

HAproxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。

注意,HAproxy与Nginx其中一个重大的区别是HAproxy就是一个纯纯的负载均衡服务器,它做不了web服务器,没法做vue单页面的载体,它是为负载而生的。

个人的感觉,HAproxy很强大,HAproxy同样是可以对请求与响应的内容做修改。除此之外HAproxy的性能比Nginx的性能更加高效,就如官网说提及的几个点,如单进程、单缓冲区、零拷贝等等优点,具体可以翻墙到官网深入了解。唯一一点要提及的是HAproxy的配置比较复杂,我第一次接触的时候是比较懵逼的,晦涩难懂。

HAproxy官网

2、HAproxy配置四层代理需要怎么做?

HAproxy使用mode tcp进行四层代理转发,以下同样也给出一个简单的配置实例

listen test1  
bind 0.0.0.0:8008  
mode tcp  
balance roundrobin  
server s1 127.0.0.1:8010 weight 1 maxconn 10000 check inter 10s  
server s2 127.0.0.1:8011 weight 1 maxconn 10000 check inter 10s  
server s3 127.0.0.1:8012 weight 1 maxconn 10000 check inter 10s  

3、HAproxy如何实现四层负载均衡?

要找到HAproxy的实现原理,真的不是一件容易的事,首先真看不懂C。但是功夫不负有人心,我从13年前它的设计文档中,找到这么一句话

How it works ?  (unfinished and inexact)
For TCP and HTTP :
- listeners create listening sockets with a READ callback pointing to the
  protocol-specific accept() function. 
- the protocol-specific accept() function then accept()'s the connection and
  instantiates a "server TCP socket" (which is dedicated to the client side),
  and configures it (non_block, get_original_dst, ...).
For TCP :
- in case of pure TCP, a request buffer is created, as well as a "client TCP
  socket", which tries to connect to the server.
- once the connection is established, the response buffer is allocated and
  connected to both ends.
- both sockets are set to "autonomous mode" so that they only wake up their
  supervising session when they encounter a special condition (error or close).

我翻译了一番,大致的意思是这样的

1、接收到请求时,进行数据处理并写入请求缓冲区
2、数据从请求缓冲区推送到上游服务器端
3、接收到服务端响应时,进行数据处理,并写入响应缓冲区
4、数据从响应缓冲区推送到客户端
(中间省略了数据处理的步骤)

后面我再一次从官方文档中,找到了相似的说明,论证了我猜想的转发过程的正确性,黏贴一下

rocessing incoming connections is by far the most complex task as it depends
on a lot of configuration possibilities, but it can be summarized as the 9 steps
below :
  - accept incoming connections from listening sockets that belong to a
    configuration entity known as a "frontend", which references one or multiple
    listening addresses;
  - apply the frontend-specific processing rules to these connections that may
    result in blocking them, modifying some headers, or intercepting them to
    execute some internal applets such as the statistics page or the CLI;
  - pass these incoming connections to another configuration entity representing
    a server farm known as a "backend", which contains the list of servers and
    the load balancing strategy for this server farm;
  - apply the backend-specific processing rules to these connections;
  - decide which server to forward the connection to according to the load
    balancing strategy;
  - apply the backend-specific processing rules to the response data;
  - apply the frontend-specific processing rules to the response data;
  - emit a log to report what happened in fine details;
  - in HTTP, loop back to the second step to wait for a new request, otherwise
    close the connection.

由上面的资料,我们得出的结论,大概可以整理出下面这样作用图

在这里插入图片描述
从图中,我们可以得出结论,这个转发过程跟Nginx是差不多的,当一个请求到达HAproxy服务时,也是要两个socket去完成转发的。

到这里,我们可以看出,请求流量,响应的流量都需要经过上述的这两个负载服务器。为什么要一直强调这一个点呢?是为了突出LVS的强大的黑科技。

三、LVS

1、LVS是什么?

LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。在精读了LVS的中文官方文档之后,我自己给它下这么一个定义——“利用Linux系统能力做负载均衡”。

为什么这么说呢?是因为看了LVS的搭建过程,几乎都是配置Linux的网卡…等去做一个转发的动作。

LVS天生就是来做负载均衡的,这里你就不用再去想LVS能不能也对请求、响应的内容提取处理,肯定是不行的,正因为LVS非常透明的转发,流量到它,它透明的转发过去,才有这么高效的性能。

2、LVS配置四层代理需要怎么做?

这个就不摘抄了,过程比较复杂,笔者也还没实施过,可以通过其他途径进行了解,也不是本篇文章的重点。

3、LVS如何实现四层负载均衡?

LVS官方文档称其它的技术为IP负载均衡技术,包括

VS/NAT是通过网络地址转换(Network Address Translation)将一组服务器构成一个高性能的、高可用的虚拟服务器。

VS/TUN是通过IP隧道实现虚拟服务器。

VS/DR是通过直接路由实现虚拟服务器。

个人认为VS/NAT是作用在四层的(虽然是对IP进行替换,但是也有可能替换了端口,端口的概念是四层的)。而其他两种像是作用在三层的,因为其他两种转发模式中,你已经看不到TCP段了(见下文)。

下面,我们分别细看其转发的方式,并思考跟Nginx与HAproxy到底有什么区别。

3.1、NAT技术

NAT是网络地址转换技术,将内部IP地址转化为Internets上可用的外部地址。

NAT的工作原理是报文头(目标地址、源地址和端口等)被正确改写后,客户端相信它们连接一个IP地址,而不同IP地址的服务器组也认为它们是与客户直接相连的。由此,可以用NAT方法将不同IP地址的并行网络服务变成在一个IP地址上的一个虚拟服务。

客户端通过Virtual IP Address(虚拟服务的IP地址)访问网络服务时,请求报文到达调度器,调度器根据连接调度算法从一组真实服务器中选出一台服务器,将报文的目标地址Virtual IP Address改写成选定服务器的地址,报文的目标端口改写成选定服务器的相应端口,最后将修改后的报文发送给选出的服务器。

同时,调度器在连接Hash表中记录这个连接,当这个连接的下一个报文到达时,从连接Hash表中可以得到原选定服务器的地址和端口,进行同样的改写操作,并将报文传给原选定的服务器。

当来自真实服务器的响应报文经过调度器时,调度器将报文的源地址和源端口改为Virtual IP Address和相应的端口,再把报文发给用户。我们在连接上引入一个状态机,不同的报文会使得连接处于不同的状态,不同的状态有不同的超时值。在TCP连接中,根据标准的TCP有限状态机进行状态迁移。

以下是一个转换的例子

在这里插入图片描述

VS/NAT的配置如下表所示,所有到IP地址为202.103.106.5和端口为80的流量都被负载均衡地调度的真实服务器172.16.0.2:80和172.16.0.3:8000上。目标地址为202.103.106.5:21的报文被转移到172.16.0.3:21上。而到其他端口的报文将被拒绝。

ProtocolVirtual IP AddressPortReal IP AddressPortWeight
TCP202.103.106.580172.16.0.2801
172.16.0.380002
TCP202.103.106.521172.16.0.3211

从以下的例子中,我们可以更详细地了解报文改写的流程。

访问Web服务的报文可能有以下的源地址和目标地址:

SOURCE202.100.1.2:3456DEST202.103.106.5:80

调度器从调度列表中选出一台服务器,例如是172.16.0.3:8000。该报文会被改写为如下地址,并将它发送给选出的服务器。

SOURCE202.100.1.2:3456DEST172.16.0.3:8000

从服务器返回到调度器的响应报文如下:

SOURCE172.16.0.3:8000DEST202.100.1.2:3456

响应报文的源地址会被改写为虚拟服务的地址,再将报文发送给客户:

SOURCE202.103.106.5:80DEST202.100.1.2:3456

这样,客户认为是从202.103.106.5:80服务得到正确的响应,而不会知道该请求是服务器172.16.0.2还是服务器172.16.0.3处理的。

根据上面的叙述,我们可以画出这样一张图。

在这里插入图片描述

从上图可以看出,单靠这种模式,已经能比Nginx和HAproxy高效了,人家直接作用在包头,省去了一大波处理的动作,还能不节省时间么?

你以为LVS就这样了,还不仅仅如此。

3.2、TUN技术

在VS/NAT的集群系统中,请求和响应的数据报文都需要通过负载调度器。如果能将请求和响应分开处理,即在负载调度器中只负责调度请求而响应直接返回给客户,将极大地提高整个集群系统的吞吐量。

IP隧道(IP tunneling)是将一个IP报文封装在另一个IP报文的技术,这可以使得目标为一个IP地址的数据报文能被封装和转发到另一个IP地址。IP隧道技术亦称为IP封装技术(IP encapsulation)。IP隧道主要用于移动主机和虚拟私有网络(Virtual Private Network),在其中隧道都是静态建立的,隧道一端有一个IP地址,另一端也有唯一的IP地址。

LVS利用IP隧道技术将请求报文封装转发给后端服务器,响应报文能从后端服务器直接返回给客户。但在这里,后端服务器有一组而非一个,所以我们不可能静态地建立一一对应的隧道,而是动态地选择一台服务器,将请求报文封装和转发给选出的服务器。这样,我们可以利用IP隧道的原理将一组服务器上的网络服务组成在一个IP地址上的虚拟网络服务。

这里可以回想到大佬说的那个模式,其实大佬想表达的就是用的过IP隧道实现的虚拟服务器。

在这里插入图片描述

在这里插入图片描述

这里,需要注意的是,LVS上面的TCP状态,不再是标准的的TCP状态迁移,而是半连接状态,它的变迁如下:

在这里插入图片描述

由上面的叙述,我们同样可以得到这样的一张作用图:

在这里插入图片描述

从这里可以看出,LVS的基于IP隧道转发模式,直接对TCP的包进行封装,丢给上游的服务器,上游服务器对封装后的包进行拆解获取到最原始的包,进行处理,最后直接响应到客户端,这就是为什么能响应不经过LVS的原因。下面的DR模式其实跟IP隧道的模式是差不多的。

3.3、DR技术

跟VS/TUN方法相同,VS/DR利用大多数Internet服务的非对称特点,调度器和服务器组都必须在物理上有一个网卡通过不分段的局域网相连,即通过交换机或者高速的HUB相连,中间没有隔有路由器。VIP地址为调度器和服务器组共享,调度器配置的VIP地址是对外可见的,用于接收虚拟服务的请求报文;所有的服务器把VIP地址配置在各自的Non-ARP网络设备上,它对外面是不可见的,只是用于处理目标地址为VIP的网络请求。

它的连接调度和管理与VS/NAT和VS/TUN中的一样,它的报文转发方法又有不同,将报文直接路由给目标服务器。在VS/DR中,调度器根据各个服务器的负载情况,动态地选择一台服务器,不修改也不封装IP报文,而是将数据帧的MAC地址改为选出服务器的MAC地址,再将修改后的数据帧在与服务器组的局域网上发送。因为数据帧的MAC地址是选出的服务器,所以服务器肯定可以收到这个数据帧,从中可以获得该IP报文。当服务器发现报文的目标地址VIP是在本地的网络设备上,服务器处理这个报文,然后根据路由表将响应报文直接返回给客户。

在这里插入图片描述

VS/DR负载调度器也只处于从客户到服务器的半连接中,按照半连接的TCP有限状态机进行状态迁移。

根据上述描述,我们同样也是画出这样的一张图:

在这里插入图片描述

从上面可以看出,DR模式,是直接作用在数据链路层的,人家是直接替换MAC地址的,这种转发模式不需多言了吧。

四、总结

从上面文章所论述的,我们可以得到以下结果:

Nginx、HAproxy转发:接收TCP请求、建立TCP请求、转发TCP请求,接收响应,转发响应。

LVS转发:接收TCP请求、直接换头、转发TCP请求、结束。

这也就是我想说当做四层负载的时候,LVS比Nginx、HAproxy高效的原因。

虽然LVS性能高,那我能用LVS替代掉Nginx、HAproxy吗?显然是不行的,在架构的设计中,我们还是需要根据具体场景选择最适合的负载均衡服务器。比如在背景中说的,静态资源服务器的负载,那用LVS就最适合不过了,如果你需要对HTTP的请求内容做修改,那LVS用起来就显然不是那么适合了吧?

直接替换MAC地址的,这种转发模式不需多言了吧。

从计划写这篇文章,到输出,用了两周的时间做技术的储备,各种查资料,各种了解,写文章真的不是一件容易的事情~,如果对您有帮助的话,希望得到您的支持

在这里插入图片描述

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值