其实这一节不算OpenResty的学习内容,因为负载均衡完全是由nginx完成的,不过也把它放在这里吧,谁让是一块学的呢。废话不多说,开始今天的学习任务。
首先介绍一个负载均衡,引用最佳实践里面的句子:


负载均衡(Load balancing)是一种计算机网络技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最佳化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。
使用带有负载均衡的多个服务器组件,取代单一的组件,可以通过冗余提高可靠性。负载均衡服务通常是由专用软体和硬件来完成。
负载均衡最重要的一个应用是利用多台服务器提供单一服务,这种方案有时也称之为服务器农场。通常,负载均衡主要应用于Web网站,大型的 Internet Relay Chat 网络,高流量的文件下载网站,NNTP(Network News Transfer Protocol)服务和 DNS 服务。现在负载均衡器也开始支持数据库服务,称之为数据库负载均衡器。
对于互联网服务,负载均衡器通常是一个软体程序,这个程序侦听一个外部端口,互联网用户可以通过这个端口来访问服务,而作为负载均衡器的软体会将用户的请求转发给后台内网服务器,内网服务器将请求的响应返回给负载均衡器,负载均衡器再将响应发送到用户,这样就向互联网用户隐藏了内网结构,阻止了用户直接访问后台(内网)服务器,使得服务器更加安全,可以阻止对核心网络栈和运行在其它端口服务的攻击。
当所有后台服务器出现故障时,有些负载均衡器会提供一些特殊的功能来处理这种情况。例如转发请求到一个备用的负载均衡器、显示一条关于服务中断的消息等。负载均衡器使得IT团队可以显著提高容错能力。它可以自动提供大量的容量以处理任何应用程序流量的增加或减少。
负载均衡在互联网世界中的作用如此重要,本章我们一起了解一下 Nginx 是如何帮我们完成 HTTP 协议负载均衡的。


upstream 支持的负载均衡算法

Nginx 的负载均衡模块目前支持 6 种调度算法,下面进行分别介绍:

  • 轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。 Weight
    指定轮询权值,Weight 值越大,分配到的访问机率越高,主要用于后端每个服务器性能不均的情况下。
  • ip_hash:每个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
  • fair:这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx本身是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块。
  • url_hash:此方法按访问 url 的 hash 结果来分配请求,使每个 url
    定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。Nginx 本身是不支持 url_hash
    的,如果需要使用这种调度算法,必须安装 Nginx 的 hash 软件包。
  • least_conn:最少连接负载均衡算法,简单来说就是每次选择的后端都是当前最少连接的一个 server(这个最少连接不是共享的,是每个
    worker 都有自己的一个数组进行记录后端 server 的连接数)。
  • hash:这个 hash 模块又支持两种模式 hash, 一种是普通的 hash, 另一种是一致性 hash(consistent)。

upstream 支持的状态参数

在 HTTP Upstream 模块中,可以通过 server 指令指定后端服务器的 IP 地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:

  • down:表示当前的server暂时不参与负载均衡。
  • backup:预留的备份机器。当其他所有的非 backup 机器出现故障或者忙的时候,才会请求 backup
    机器,因此这台机器的压力最轻。
  • max_fails:允许请求失败的次数,默认为 1。当超过最大次数时,返回 proxy_next_upstream 模块定义的错误。
  • fail_timeout:在经历了 max_fails 次失败后,暂停服务的时间。max_fails 可以和 fail_timeout一起使用。

注意:当负载调度算法为 ip_hash 时,后端服务器在负载均衡调度中的状态不能是 backup。

讲完了这些我们来实验一把:
先贴我的nginx配置:

upstream test.net{
        server 192.168.1.168:8000 weight=1;
        server 192.168.1.41:8000 weight=1;
    }

    server {
        listen 6699;
        location / {
            proxy_pass  http://test.net;
            proxy_set_header  X-Real-IP  $remote_addr;
        }

很简单的配置,在server外面配置了一个upstream名字叫test.net,然后当访问6699端口的时候会有一个转发请求到http://test.net;这个地址。
要说明的是upstream里面配的这两个server 192.168.1.168:8000 weight=1;server 192.168.1.41:8000 weight=1;服务是要实际存在的,用户可以自由选择你要用启个什么样的web服务。
先说一下我这边的配置:192.168.1.41这个地址是我本机上的一个centos的虚拟机配的IP地址,192.168.1.168是我本机win7系统的内网IP。
然后我分别在本机跟虚拟机上启了一个tornado的服务分别监听8000端口,程序如下:

import tornado.httpserver
import textwrap
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        greeting = self.get_argument('greeting', 'Hello')
        self.write(greeting + ', friendly user on 168!')


if __name__ == "__main__":
    tornado.options.parse_command_line()
    app    = tornado.web.Application(handlers=[(r"/",    MainHandler)])
    server = tornado.httpserver.HTTPServer( app )
    server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

两边的程序唯一不同点在于输出的时候41的地址会输出
Hello, friendly user on 41 !
而168的地址会输出
Hello, friendly user on 168!
然后启动nginx,访问http://192.168.1.41:6699/(哦,忘 了说一点,我的openresty是在虚拟机上的),结果如下:

[nginx@localhost ~]$ curl http://192.168.1.41:6699/ 
Hello, friendly user on 168!
[nginx@localhost ~]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost ~]$ curl http://192.168.1.41:6699/
Hello, friendly user on 168!
[nginx@localhost ~]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost ~]$ curl http://192.168.1.41:6699/
Hello, friendly user on 168!
[nginx@localhost ~]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost ~]$ curl http://192.168.1.41:6699/
Hello, friendly user on 168!

可以发现168跟41是交替出现的,也说明了负载均衡配置成功。
如果我们把其中一个的服务停掉,那么nginx就会自动不去访问它。
比如我们把168的服务关掉,那么结果就变成了:

[nginx@localhost conf]$ curl http://192.168.1.41:6699/    
Hello, friendly user on 41 !
[nginx@localhost conf]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost conf]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost conf]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41

这是默认的轮询的方式访问的结果,如果换方式换成ip_hash,那么配置文件变成这样:

upstream test.net{
        iphash;
        server 192.168.1.168:8000 weight=1;
        server 192.168.1.41:8000 weight=1;
    }

这样我们再访问的时候就会变成这样:

[nginx@localhost conf]$ curl http://192.168.1.41:6699/    
Hello, friendly user on 41 !
[nginx@localhost conf]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost conf]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41 !
[nginx@localhost conf]$ curl http://192.168.1.41:6699/
Hello, friendly user on 41

因为固定的IP访问会被分到固定的服务器进行响应。
其它的模式因为还没有测试过,所以先不写了,今天的学习就到这了!