对于一个每秒处理百万请求的网站来说,通常是有整个集群提供服务的,而如何做到负载均衡呢?
简介
在 GRE 中,对负载均衡是如下定义的:Traffic load balancing is how we decide which of the many, many machines in our datacenters will serve a particular request.
如何选择一个最优的解决方案实际上有多种考虑因素:从全局来看还是局部,使用硬件还是软件,所处理场景的特性等。例如,对于搜索以及视频服务来说,前者希望有较小的 RTT,而后者则希望有较大的带宽。
接下来我们看看从一个请求的处理流程来说,有哪些节点可以或者需要做负载均衡。
下图是 Amazon 的一套组网方案,首先是 DNS 系统,然后是 CDN 网络,前端的负载均衡,web 服务器,数据中心的负载均衡,应用服务器,数据库,存储等。
其中前端负载均衡之后还包括了两个可用区,用来保证高可用。
DNS
在客户端发送请求前,首先需要通过 DNS 查询域名对应的 IP 地址,这也就意味着可以首先通过 DNS 做负载均衡,而在这阶段所作的就是,尽可能返回一个最优的 IP 。
首先,最简单的方式是返回多个 A 或者 AAAA 地址,而客户端随意选择其中的一个,从而可以做到打散。
上述的方法实现非常简单,同样也带有很多问题。首先,我们很难控制客户端应该选择那个 IP 地址,当然,可以通过 SRV 设置权重以及优先级,但目前对 HTTP 请求是无效的。
另外,为了选择最近的服务器,减小 RTT,可以通过 anycast+BGP 解决,后面我们详细讨论其实现的方式。
但是还有个问题,通常的 DNS 请求并不是直接请求权威服务器的,而是直接请求的 ISP 的代理。这也就意味着,权威服务器收到的将是代理的 IP 地址,而非客户端的 IP,从而可能会导致出错。目前的解决方案是使用 EDNS 协议,它将会携带客户端的子网信息,目前 OpenDNS 是支持该功能的。
DNS 存在着一些功能限制,最明显的就是缓存、512Bytes,我们所能做的就是在做方案时铭记。
软件负载均衡
网络中常见的的负载均衡主要分为两种:一种是通过硬件来进行进行,常见的硬件有比较昂贵的 NetScaler、F5、Radware 和 Array 等商用的负载均衡器;也有类似于 LVS、Nginx、HAproxy 的基于 Linux 的开源的负载均衡策略。
商用负载均衡可以建立在四~七层协议之上,因此适用面也就更广所以有其不可替代性,而且有专业的维护团队来对这些服务进行维护,不过缺点就是花销太大,所以对于规模较小的网络服务来说暂时还没有必要使用。
对于软件实现的负载均衡,其中 LVS 是建立在四层协议上面的,而另外 Nginx 和 HAproxy 是建立在七层协议之上的。
LVS
从性能和稳定上来说 LVS 基本达到了 F5 硬件设备的 60% 性能,不过配置也最麻烦,而且健康检测需要另外配置 Ldirector 。在此简单介绍下 LVS,它配置非常简单,在内核中实现,仅做请求分发之用,可以支持几乎所有应用的负载均衡,包括 http、数据库、聊天室等。
Nginx
Nginx 工作在网络的第 7 层,所以它可以针对 http 应用本身来做分流策略,比如针对域名、目录结构等,相比之下 LVS 并不具备这样的功能。
可以负载超过 1 万的并发,除了负载均衡,还能作 Web 服务器,而且可以通过 Geo 模块来实现流量分配,不过不支持 session 保持,而且对后端 real server 的健康检查功能效果不好,而且只支持通过端口来检测,不支持通过 url 来检测。
HAproxy
其优点正好可以补充 nginx 的缺点,支持 session 保持,同时支持通过获取指定的 url 来检测后端服务器的状态。支持 TCP 模式的负载均衡,比如可以给 MySQL 的从服务器集群和邮件服务器做负载均衡。
Virtual IP Address
简单来说,用户的请求会直接发送给 VIP,而拥有该 VIP 的是一个负载均衡器,而真正处理该请求的服务器,接收的是负载均衡器发送的转发请求。
通常来说,我们希望请求发送给压力最小的服务器,不过这种方法只适用于无状态的请求。对于有状态的请求,可以将报文中的某个字段做 hash,从而一个请求只会发送到相同的服务器。
不过这样仍然存在问题,假设之前的 hash 算法是 id mod N,那么如果一台机器宕机则成为 id mod (N-1) 从而导致当前处理的连接被重置,为此就需要一种 consistent hashing 算法。