如何在Linux服务器下实现多网卡负载均衡?

还有一种负载均衡方式是在协议内部就支持负载均衡能力,也就是URL交换或者叫七层交换,它提供了一种对访问流量的高层控制手段。Web内容交换技术会检查所有的HTTP报头,根据报头里的信息来做负载均衡决策。比如它能根据这些信息决定怎么为个人主页、图像数据之类的内容提供服务,常见的有HTTP协议里的重定向能力等等。

HTTP是跑在TCP连接最高层的。客户端通过固定的80端口TCP服务直接连到服务器,然后通过这条TCP连接往服务器发一个HTTP请求。协议交换是按内容策略来控制负载的,而不是看TCP端口号,所以不会造成访问流量滞留。因为负载平衡设备要把进来的请求分配给多台服务器,所以它只能在建立TCP连接、并且HTTP请求过来之后才能确定怎么平衡负载。当一个网站的点击率达到每秒上百甚至上千次时,TCP连接、HTTP报头信息的分析还有进程的时延,这些就变得很关键了,必须想尽办法提升这几部分的性能。

其实HTTP请求和报头里有不少对负载平衡有用的信息。我们能从里面知道客户端请求的URL和网页,靠这个信息,负载平衡设备就能把所有图像请求都引到一台图像服务器,或者根据URL的数据库查询内容去调用CGI程序,把请求引导到一台专用的高性能数据库服务器上。如果网络管理员熟悉内容交换技术,他还能根据HTTP报头里的cookie字段来改善对特定客户的服务;要是能从HTTP请求里找出一些规律,还能拿来做各种决策。当然,除了TCP连接表这个问题之外,怎么找到合适的HTTP报头信息、怎么做出负载平衡决策,是影响Web内容交换技术性能的两个重要问题。假如Web服务器已经针对图像服务、SSL对话、数据库事务服务这类特殊功能做过优化,那么在这个层次上做流量控制就能提升网络性能。

说到Linux服务器多网卡负载均衡,有一种思路是应用服务器层面的负载均衡。做法是把客户端的负载均衡层移植到某个中间平台,形成三层结构,这样一来客户端应用不用做特殊改动,就能透明地通过中间层应用服务器把请求均衡分配到各个服务结点上。比较常见的实现手段就是反向代理技术,用反向代理服务器把请求均匀转发给多台服务器,或者干脆直接把缓存的数据返回给客户端,这种加速模式在一定程度上能提升静态网页的访问速度,也就顺带达到了负载均衡的目的。

用反向代理有个好处,就是能把负载均衡和代理服务器的高速缓存技术结合起来,性能收益不错。不过它自身也有毛病,首先每种服务都得专门开发一个反向代理服务器,这可不是件轻松的活儿。而且反向代理服务器虽然效率能做得挺高,但每代理一次,服务器就得维护两个连接,一个对外、一个对内。所以遇到连接请求特别高的场景,代理服务器的负载就非常大了。它倒是能执行针对应用协议优化过的负载均衡策略,每次只挑最空闲的内部服务器来服务,可随着并发连接数一涨,代理服务器自己的负载也水涨船高,最后它本身反而变成服务的瓶颈了。

再聊聊基于域名系统的负载均衡,也就是大家常说的DNS轮询。NCSA的可扩展Web算是最早用动态DNS轮询技术的web系统了。原理并不复杂,就是在DNS里给多个地址配置同一个名字,客户机查询这个名字时就会拿到其中一个地址,这样不同客户自然就访问了不同的服务器,达到负载均衡的效果。很多知名网站都用过这招,像早期的yahoo、163等等。动态DNS轮询实现简单,不用复杂的配置和管理,一般支持bind8.2以上的类unix系统都能跑,所以用得非常广。

不过DNS负载均衡虽然简单有效,问题也不少。第一,域名服务器根本不知道服务结点是不是还活着,如果某个结点失效了,域名系统照样把域名解析到它上面,用户访问就失败了。第二,受DNS数据刷新时间TTL的影响,一旦超过这个TTL,别的DNS服务器就要来和它交互重新拿地址数据,可能就拿到不同的IP了。所以想让地址随机分配,就得把TTL设得尽量短,好让各地的DNS服务器都能更新对应地址,实现随机获取。可TTL设太短又会让DNS流量暴增,反倒带来额外的网络麻烦。第三点,它分不清服务器之间的差异,也反映不了服务器当前的运行状况。用DNS负载均衡时得尽量保证不同客户能均匀拿到不同地址,可现实中用户A可能只浏览几个网页,用户B却在疯狂下载,域名系统没有合适的负载策略,只是简单轮流,就很容易把A的请求发到负载轻的站点,却把B的请求发到已经很重的站点上。所以在动态平衡这点上,动态DNS轮询的效果其实并不理想。

最后说说网络接入协议交换这块。大型网络一般是由大量专用技术设备拼起来的,比如防火墙、路由器、第三层和第四层交换机、负载均衡设备、缓冲服务器还有Web服务器等等。怎么把这些技术设备有机地组合到一起,是直接关系到网络性能好坏的关键问题。现在不少交换机都提供第四层交换功能,对外只给出一个统一的IP地址,对内则映射成多个内部IP地址。对每一次TCP和UDP连接请求,它会根据端口号,按照事先定好的策略动态挑一个内部地址,把数据包转发到那个地址上去,这样就达到了负载均衡的目的。