《构建高性能的web站点》读书笔记--负载均衡

时间:2023-01-31 19:48:35

     很多的网站一开始并不需要太大的规模,但是做为网站设计者从一开始就必须考虑到扩展,做一个可扩展性强的架构。所谓可扩展性就是系统通过规模的扩展来提高系统的承载能力,毕竟服务器自身的垂直扩展很快就会受到制约,单机很快便不能满足我们的需求,因此这种能力往往通过增加物理服务器或集群节点等方面来实现,这种能力越强,承载能力可提升的空间也越大。而web站点的水平扩展,负载均衡是一种最常见的手段。下面介绍一下几种实现负载均衡的方式。

HTTP重定向

      HTTP重定向,相信对于所有web程序员都不陌生,例如我们请求某个页面时,被转向登录页,登录页面之后又被转到了某个页面。大致来说就是,浏览器请求某个URL后,服务器通过HTTP响应头信息中的Location标记返回一个新的URL,这样浏览器主会继续请求这个新的URL,完成自动跳转。也正是因为HTTP重定向有请求转移和自动跳转的能力,所以我们就可以用它来实现负载均衡以实现WEB扩展。

      例如我们访问在浏览器输入http://www.google.com/时,你就会收到302的响应,并且重定向到Location标记的URL,经过两次重定向最后你被送到http://www.google.com.hk/的页面,随后的请求都会在这个新定向的域名下进行。这样不仅分散了请求,还实现了就近访问,缩短网络传输的时间。

以下我们经常见到的重定向的调度策略,来分析下如何实现优良的调度策略:

1,按区域重定向,例如上面的google会把来自中国的请求转身到http://www.google.com.hk/,还有我们可以检测用户来自电信或网通,从而转向相应网络的服务器,实现最近服务。但是有些时候通过地域来划分就不那么合理,即便是同一区域,也可能是多台的服务器来共同服务,需要其它负载均衡的方式。

2,随机分配。我们维护一个可用的服务器的列表,每接到一个请求,从列表中随机一个服务器来提供服务,从整体来看各个服务器可以平均的分配请求。

3,依次轮询。所有请求按照一定的顺序依次转移给各个服务器,来实现绝对意义上的平均分配。但是这样,我们必须维护上个请求的序号,以便在各个http请求到达时都可以访问,并且这个序号在同一时间只能有一个请求去修改,这样的策略对于高并发的web请求可以说是一个灾难。

     因此,我们的策略里尽量让每一个请求不依赖于外部条件,自己能计算出服务器序号并且足够分散,效率就会比较高。例如根据用户IP的散列来计算服务器的序号,这样不仅不会有依次轮询的代价,并且该用户每次的请求都会被同一个服务器处理,一些用户状态就易处理。

可是在实际的环境中,这样能做到真正的均衡吗?

      这里我们暂且把处理请求转移的服务器称为主服务器,从以上我们可以看到,主服务器只能保证到达它的请求被均匀的分配,但是用户在被重定向后在实际处理服务器下会停留多久,访问多少页面,主服务器是无法知道的;有些用户可能会保存这些实际处理服务器地址,以后的访问绕过了转移服务器等等,如此多的因素,都可能导致不能真正的负载均衡,但是对于那些文件下载、广告展示等这些只有一次性请求,主站点的程序始终把握着控制权,是可以保证负载均衡的。

它的瓶颈主要在哪里呢?

      另外,要考虑请求处理开销和请求转移的开销,请求处理的开销越大,越适合适用重写向,例如下载,主站点在迅速处理完转向之后,就可以去处理其它的请求,而实际下载却是要消耗一定的时间的。如果请求处理的开销很小,那么主服务器很忙时,实际处理的服务器就会很空闲,造成资源的浪费。

      当所有的请求都要经过主服务器才能各个实际处理服务器时,主服务器就成为了我们系统的中心,它的能力很大程度上决定的系统的能力,当请求高到不能及时转移时,我们就应该考虑下其它的策略了。

DNS负载均衡

      我们知道DNS是负责解析域名的,当我们用域名访问站点时,实际上都会经过DNS服务器来获取域名指向的IP,实际上DNS服务器完成了域名到IP的映射,同样这个映射可以是一对多的,也就是DNS可以把对域名的请求按照一定的策略分配到不同的服务器上,这样我们就可以依此来实现负载均衡。貌似和HTTP重定向很样,但是实现的机制却是完全不同的。(在window下可以用nslookup命令查询域名对应的IP地址列表,这个命令会返回离你最近的DNS服务器缓存的记录并不一定是全部)

      与基于重定向的负载均衡方式相比,DNS方案节省了所谓的主站点或是替代了主站点的职能,但是无论你采用的是DSN服务商还是自己的DNS服务器,几乎不用担心DNS服务器的性能问题,事实上,DNS记录会被用户浏览器或互联网接入服务商的各级DNS服务器缓存,只有缓存过期才会重新向该域名的DNS服务器请求解析,所以即使是采用依次轮询的调度策略,我们几乎不会遇到DNS服务器成为性能瓶颈的问题。

      尽管DNS的负载均衡策略几乎没有HTTP重定向的制约,但也没有了重定向的灵活性。毕竟HTTP重定向是我们自己程序实现的,我们可以根据业务的需要制定最适合的策略,我们甚至可以在充分理解http内容之后对URL进行合理的过滤或转移,而为DNS服务器开发自定义的调度策略就不是那么容易了,并且无法把http内容引入到策略的开发,不过值得庆幸的是,DNS服务器也提供了一些调度策略供你选择,如根据IP的智能解析策略,可在列表中寻找离用户最近的一台服务器,但是相对于重定向的灵活性就差太远了。

     另外,DNS做为一个请求的调度器,满足了请求分配的均衡时,却无法把各个服务器的负载能力、服务器目前的负载状况等因素考虑在内,可能并不能实现真正的负载均衡。还有面对故障的反应能力,如果是HTTP重定向我们可以自动的维护可用服务器列表并且让它立即生效,而对DNS的负载系统,我们不得不去修改DNS的映射,而一般情况下这需要经过一段时间才能有效。

反向代理负载均衡

     我们之前的缓存介绍中,介绍过反向代理服务器,它同样可以作为调度器来实现负载均衡系统。反向代理服务器核心工作是转发HTTP请求,它工作在HTTP层面,也就是TCP七层中的第七层应用层,因此基于反射代理的负载均衡系统也称为七层负载均衡。目前几乎所有主流WEB服务器都支持基于反向代理的负载均衡,因此实现它并不困难。

     相对于我们介绍的前两种方式,它们其实是请求的转移,而反向代理请求的转发:所有的请求都要经过反向代理服务器统一调度,并且要等待实际请求处理服务器响应,然后再将响应内容返给用户。

     这种方式几乎可以解决基于DNS的负载均衡的所有的不足:首先它是HTTP层面的转发,可以理解HTTP内容之后做出合适的处理;很多的反向代理服务器都可以设置各个实际服务器的权重,意思就是说我们可以不必再所谓的均匀分配,实现“能力越大,责任也越大”,根据服务器实际能力分配合适的工作量;我们可以对实际服务器的健康状况做出检测,可以做出及时的处理。

      但同时也具备了重定向的不足。请求的转发是需要开销的,如创建线程,与后端服务器建立连接、接收实际服务器返回的结果等等,如果请求处理的开销很小,那么转发的开销就会特别的明显,并且对于文件下载这种重定向最胜任的需求,对反向代理来说基本上是个灾难;同样做为一个任何请求都要“过问”的反向代理,它的性能很大程度上决定的系统的性能,扩展就会就到制约。

IP负载均衡

      基于IP负载均衡的系统工作在传输层,会对数据包中的IP地址和端口信息进行修改,所以也称为四层负载均衡。它会在数据到达应用层之前,已完成转发,因些这工作都是由系统内核来完成,应用程序对此无能为力,当然性能来说也会很大的提升。

      简单介绍下工作原理:调度服务器有两块网卡,一块对外网,配置外网IP,负责接收用户请求;另一块配置内网IP,负责把请求转发给内部网络的实际服务器,并接收数据。实际服务器全部处于内网,对外不可见,只须配置内网IP,但是它们的默认网关必须是调度服务器的内容IP。这样用户请求的数据包,在到达调度服务器时,它对数据包进行修改,把目标地址修改为内网实际处理服务IP,服务器在处理完之后,会根据数据包的来源地址来确定目标地址进行发送,不过它们必须经过默认网关,也就是调度服务器,这时它再次对数据包进行修改,把来源地址换成自己的外网IP,这样就完成了一个请求过程。

      这时,调度器再次成为整个系统的关键点,它决定了系统的性能和扩展性。

直接路由

      不同于IP负载均衡,直接路由负载均衡高度器工作在数据链路层,它通过修改数据包的目标MAC地址,将数据包转发到实际服务器,不同的是,这些处理的结果直接发送给用户,不再经过调度器。这时我们的实际服务器必须直接连接到外网,并且不在以调度服务器为默认网关。

      这里有一个IP别命的东西,意思就是说你可以为一个网卡配置多个IP地址,并且对应同一个MAC地址。这样,我们分别为调度器服务器和实际处理服务器分别配置外网IP之后,还要配置一个相同的IP别名(另一个IP地址),并且把这个IP绑定到域名,然后把IP别名添加到回环接口LO上,并设置路由规则,让调度器只寻找特定拥有这个别名的服务器,并且让实际服务器禁止响应来自网络中会对IP别名的ARP广播。这样当请求到达调度器时,它只修改数据包的MAC地址,转发给实际服务器,因为调度器和实际服务器拥有的别名,因此实际服务器可以处理这些数据包,然后把响应直接返回给用户。

IP隧道

     简单的说,就是调度器将收到的数据包封装到一个新的IP数据包中,转交给实现服务器,然后实际服务器可以处理数据包直接响应客户端。

 

     由于后三种方式,书中主要是基于linux系统内核,体会较少,只是简单的介绍下了一下在理解范围之内的实现原理,如有想深入理解,看看原著或查下其它资料吧。其实在负载均衡实现了规模扩展和单点故障等问题之后,就面临到另一个难题,如果用户的状态或其它状态数据能用cookie和之前的缓存中提到的分布式缓存来解决的话,那么文件如何来实现共享呢?下篇分享文件的共享和内容的分发同步。