spice and wolfspice and wolf Be the One you wanna Be

nginx负载均衡

服务器高可用越来越成为必不可少的需求之一,负载均衡就是达成高可用环境的一个重要因素,使用nginx能方便的实现复杂的负载均衡需求,所以nginx负载均衡技术将是我们这张的重点课题之一。在实现了负载均衡后,一个难点就是如何保持用户状态信息,这将是本章需要重点关注的第二个课题。最后一个课题是nginx的运行状态监测,分为被动监测和主动监测,它能监测上游服务器的运行状态并自动相应让服务器能正常运行。

负载均衡

nginx中三种协议的负载均衡

HTTP负载均衡

# HTTP上游
http {
    upstream backend {
        # 默认权重1
        server 10.10.12.45:80        weight=1;
        server app.example.com:80    weight=2;
        # 备用服务器
        server spare.example.com:80    backup;
    }

    server {
        location / {
            proxy_pass http://backend;
        }
    }
}

http块中的upstream块定义了上游HTTP服务器的地址,这些地址包括了Unix socket、ip地址和完全限定域名。在此基础上可以加上一些可选参数,包括权重(weight)、是否可用(down)、是否备用(backup)、最大失败次数(max_fails)和失败重试时间(fail_timeout)

TCP负载均衡

stream {
    upstream mysql_read {
        server read1.example.com:3306 weight=5;
        server read2.example.com:3306;
        server 10.10.12.34:3306       backup;
    }

    server {
        listen 3306;
        proxy_pass mysql_read;
    }
}

stream块中的upstream块定义了上游TCP服务器的地址。

UDP负载均衡

stream {
    upstream ntp {
        server ntp1.example.com:123 weight=2;
        server ntp2.example.com:123;
    }
    
    server {
        listen 123 udp;
        proxy_pass ntp;
    }
}

stream块中的upstream块定义了上游UDP服务器的地址,和TCP的区别在于server监听端口后加上了udp参数。在后面继续加上reuseport参数后,允许多个进程/线程bind/listen相同的IP/PORT。

stream {
    server {
        listen 1195 udp reuseport;
        proxy_pass 127.0.0.1:1194;
    }
}

负载均衡方法

  1. 轮询法(默认的负载均衡方法)。默认的负载均衡方法,请求会被轮流发送到每个上游服务器上。
  2. 最小连接数法(least_conn)。会选择积压连接数最少得一台服务器来处理当前请求。
  3. 最快响应时间(least_time)。基于最小平均响应时间进行负载均衡。
  4. 一般哈希法(hash)。根据请求或运行时变量进行负载均衡,可以用于进行细粒度流量控制的场景。
  5. 加权随机法(random)。按照权重随机请求上游服务器,而不是顺序。
  6. IP哈希(ip_hash)。根据IP地址进行负载均衡,同一个IP地址将负载到同一个上游服务器。
upstream backend {
    # 示例:最小连接数法
    least_conn;
    server backend.example.com;
    server backend1.example.com;
}

状态变量保持

分为cookie和session的细粒度控制。

使用nginx plus的粘性cookie特性

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    sticky cookie
           affinity
           expires=1h
           domain=.example.com
           httponly
           secure
           path=/;
}

粘性cookie配置设置后,每当有一个客户端初次请求上游服务时,nginx plus会创建一个cookie,并通过该cookie将下游客户端和一个上游服务器绑定,目的是让其后续请求都发往同一个上游服务器。可以在该配置中添加额外的属性,例如cookie名(示例中的affinity),过期时间(expires),是否能被客户端消费(httponly),是否只能通过https方式发送(secure),有效路径(path)。

跟踪已有的服务器session

upstream backend {
    server backend1.example.com:8080;
    server backend2.example.com:8081;

    sticky learn
           create=$upstream_cookie_cookiename
           lookup=$cookie_cookiename
           zone=client_sessions:2m;
}

当用户发送请求到服务器的web应用时,我可以用上面的配置来跟踪服务器session。配置了sticky learn指令后,当应用在某个响应中创建并存储了session状态信息的cookie时,nginx plus会发现并跟踪他们;跟踪时所使用的内存可以通过zone指令来指定;通过使用create指令,可以让nginx plus在上游的响应中寻找cookies,这些参数都由HTTP块暴露出来。

路由控制

map $cookie_jsessionid $route_cookie {
    ~.+\.(?P<route>\w+)$ $route;
}

map #request_uri $route_uri {
    ~jsessionid=.+\.(?P<route>\w+)$ $route;
}

upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;

    sticky route $route_cookie $route_uri;
}

细粒度路由控制

Connection Drainning

有一种需求是,要让客户在无感知的情况下更新系统,这时就要启动新服务并关掉旧服务,但如果直接关掉旧服务会影响客户体验,因为会让session丢失,这就可能出现让本已经通过验证的用户需要再次登录的一些情况。所以,为了优雅地关闭服务器,我们需要对旧服务进行connection drainning操作。

$ curl -X POST -d '{"drain":true}' 'http://nginx.local/api/3/http/upstreams/backend/servers/0'

响应信息为:

{
    "id":0,
    "server":"172.17.0.3:80",
    "weight":1,
    "max_conns":0,
    "max_fails":1,
    "fail_timeout":"10s",
    "slow_start":0s,
    "route":"",
    "backup":false,
    "down":false,
    "drain":true
}

执行完指令后,相应服务会停止建立连接,并持续运行一段时间直到所有session耗尽。上面是用nginx plus API的方式去截流,还能修改并reload热加载服务的方式实现截流。

服务被动健康检查

当你需要被动的检查上流服务的运行状态时,可以使用下面的指令。

upstream backend {
    server backend1.example.com:1234 max_fails=3 fail_timeout=3s;
    server backend2.example.com:1234 max_fails=3 fail_timeout=3s;
}

被动健康检查中的server指令适用于HTTP,TCP和UDP;且被动检查是默认设置,默认的max_fails为1,fail_timeout为10s。即当请求失败达到1个时,就会标记相应上游服务异常,nginx会暂时停止将请求转发到相应上游地址,但每隔10s,nignx会发送少量请求来探测其健康状态。

主动健康检查(nginx plus)

http {
    server {
        #...
        location / {
            proxy_pass http://backend;
            health_check interval=2s;
                fails=2
                passes=5
                uri=/
                match=welcome;
        }
    }
    # status is 200, content type is "text/html",
    # and body contains "Welcome to nginx!"
    match welcome {
        status 200;
        header Content-Type = text/html;
        body ~ "Welcome to nginx!";
    }
}

这是http的健康检查配置,表示通过每两秒发送HTTP get请求到/请求路径,来确认上游服务的健康状态,如果连续5个响应符合要求,nginx就会认为上游服务是健康的,如果连续两个请求都失败了,nginx就会认为上游服务健康状态异常。请求如何才能符合要求呢,这就要响应能匹配welcome块,匹配的参数分三种,一种是响应状态(status),第二种是请求头信息,第三种是请求体,此示例中表示响应中状态信息需要是200,响应头需要时text/html,响应体需要时Welcome to nginx!。

stream {
    # ...
    server {
        listen 1234;
        proxy_pass stream_backend;
        health_check interval=10s;
            passes=2;
            fails=3;
            match=welcome;
        health_check_timeout 5s;
    }
    match welcome {
        send "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n";
        except ~* "200 OK";
    }
    # ...
}

这是一个TCP主动健康检查配置,表示nginx监听1234端口,并将请求代理到stream_backend上游服务,检查间隔为10秒,每两个连续成功请求表示健康,每三个连续失败请求表示异常,异常后会每5秒探查异常服务的健康状态。匹配规则在welcome块中定义,主要有两个参数,一个是send,一个是except,如果匹配块中两个参数都没有,表示只测试是否能连接到服务器,连接到则符合匹配要求;如果只有一个except参数,则期望服务器会无条件发送数据;如果纸质定了send参数,则期望在连接建立成功的基础上,指定的字符串会成功发送到上游服务;如果同时指定,则基于send参数发送的请求需要与except参数指定的正则表达式匹配。

慢启动(nginx plus)

当你希望你的服务在满载之前能经历一个慢速启动过程,则可以采用这个配置。

upstream {
    zone backend 64k;
    
    server server1.example.com slow_start=20s;
    server server2.example.com slow_start=15s;
}

表示至少通过20秒,server1.example.com才会慢慢提升其连接数;同样,至少通过15秒,server2.example.com才会慢慢提升其连接数。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Press ESC to close