nginx负载均衡服务器配置

多个应用实例之间的负载均衡是一种优化资源常用的技术。是一种使流量最大化,降低延迟、容错的配置。
nginx可以作为一个高效的HTTP负载均衡服务器,用于将多个应用服务器的流量进行分配以提高性能扩展性及可靠性。

负载均衡的方法
下面的负载均衡机制(或方式)被nginx所支持:

  • 轮询:请求以轮询的形式传给应用服务器
  • 最少连接:下一个请求会分配给活跃连接数最少的服务器
  • ip哈希表:使用哈希函数决定下一个请求由哪个服务器处理(基于客户端的IP)

默认负载均衡服务器配置
最简单的负载均衡服务器的配置如下:

http {
    upstream myapp1 {
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

在上面的例子中,有三个同样的应用实例在srv1-srv3上。当负载均衡的方法没有指定,默认是用轮询的方法。所有请求都会代理到服务器组myapp1上,nginx应用HTTP负载均衡分配请求。
nginx实现的反向代理包括HTTP、HTTPS、FastCGI、uwsgi、SCGI和memcached.
配置HTTPS的负载均衡,用https作为协议。
配置FastCGI、uwsgi、SCGI或memcached,分别使用fastcgi_pass, uwsgi_pass, scgi_pass, 和memcached_pass指令。
最少连接负载均衡
另一种负载均衡的规则是最少连接。最少连接规则允许使用更公平的方式控制负载,特别对于一些耗时较长的请求。
当least_conn指令用在服务器组配置中时,nginx会启用最少连接负载均衡:

upstream myapp1 {
    least_conn;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

会话持久化
请注意,轮询法和最小连接法负载均衡,所有随后的客户端请求可能会被分配到不同的服务器上。不能保证每个客户端的请求总是转发到同一个服务器。
如果有需要将一个客户端与特定的应用服务器进行绑定,换句话说,使客户端会话固定或持久化,可以使用ip-hash机制负载均衡。
使用ip-hash机制,客户端IP用作哈希关键词来决定使用哪个服务器组处理请求。这个方式确保了相同的客户端的请求总是转发到相同的服务器除非服务器不可用。
配置ip-hash负载均衡,只需要加上ip_hash指令到服务器组:

upstream myapp1 {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

带权重的负载均衡
可以通过服务器权重来进一步的影响nginx的负载均衡算法。
上面的例子中,没有配置服务器的权重,这意味着所有服务器的权重都是相同的。
在轮询方式中,权重意味着更多或更少的请求分配到服务器上。
当weight参数在一个server后定义了,权重会作为负载均衡的考量因素。

upstream myapp1 {
    server srv1.example.com weight=3;
    server srv2.example.com;
    server srv3.example.com;
}

在这个配置下,每5个新的请求会在应用实例中如下分配:3个请求转发到srv1,一个请求转发到srv2,另一个转发到srv3.
在最近的nginx版本中可以用类似的方法配置最少连接及ip-hash负载均衡的权重。
健康监测
nginx实现的反向代理包括同频带信号传输(或被动)服务器健康监测。如果一个服务器返回了错误,nginx将会标记这个服务器为失败。然后会在一段时间内避免向这个服务器转发请求。
max_fails指令设置了在fail_timeout期间与服务器之间进行的不成功的通信次数。默认情况下,max_fails为1。当设为0时,此服务器的健康检查被关闭。fail_timeout参数定义了多长时间服务器会被标记为失败的。在fail_timeout间隔结束后,nginx会开始优雅的通过客户端请求探测服务器。如果探测成功,服务器会被标记为可用。

nginx配置之server_name

介绍nginx中server_name的用法。

服务器名称通过server_name指令定义,用于决定请求由哪个server块进行处理。可以用具体的名称、通配符名称或正则表达式定义。

server {
    listen       80;
    server_name  example.org  www.example.org;
    ...
}

server {
    listen       80;
    server_name  *.example.org;
    ...
}

server {
    listen       80;
    server_name  mail.*;
    ...
}

server {
    listen       80;
    server_name  ~^(?.+)\.example\.net$;
    ...
}

当通过名称查询一个虚拟主机时,如果名称匹配了超过一个的变量,例如通配符和正则都匹配,则先匹配上的变量会被选中,优先级顺序如下:

  1. 具体的名称
  2. 以*开头的最长的通配符
  3. 以*结尾的通配符
  4. 第一个匹配的正则表达式(按出现在配置文件中的先后顺序)

通配符
通配符的*只能出现在名称的开头或结尾,并且只能使用英文句号“.”作为边界。名称“www.*.example.org”或“w*.example.org”是无效的。但这些名称可以通过正则表达式定义,例如“~^www\..+\.example\.org$”及“~^w.*\.example\.org$”。一个*可以匹配多个名字部分。名字“*.example.org”不仅匹配www.example.org还匹配www.sub.example.org
正则表达式
nginx使用的Perl正则表达式语法(PCRE),要用正则表达式,必须以~开头:
server_name ~^www\d+\.example\.net$;
否则会被认为是一个确切的名称,如果存在*,还被认为是统配符(这往往是不对的)。不要忘记添加“^”和“$”,语法上不要求加,但加上更符合逻辑。另外,注意域名的点应该用“\”进行转义。含有“{”和“}”的正则表达式需要用引号包裹:
server_name "~^(?\w\d{1,3}+)\.example\.net$";
否则,nginx启动会报错:
directive "server_name" is not terminated by ";" in ...
名称的正则表达式的捕获信息可以用作变量:

server {
    server_name   ~^(www\.)?(?.+)$;

    location / {
        root   /sites/$domain;
    }
}

PCRE库支持的用法如下:

? Perl 5.10 compatible syntax, supported since PCRE-7.0
?’name’ Perl 5.10 compatible syntax, supported since PCRE-7.0
?P Python compatible syntax, supported since PCRE-4.0

如果nginx启动的时候报如下错误:
pcre_compile() failed: unrecognized character after (?< in ...
说明PCRE库比较老,试试改成?P语法。捕获信息也可以用数字代替:

server {
    server_name   ~^(www\.)?(.+)$;

    location / {
        root   /sites/$2;
    }
}

但是,这种用法仅限使用在比较简单的案例中,因为用数字代替很容易被覆盖重写。
混合名称
有一些特殊处理的服务器名称。
如果需要处理http请求没有Host域,并且没有设置默认服务器,需要定义一个空的名称:

server {
    listen       80;
    server_name  example.org  www.example.org  "";
    ...
}

如果server块中没有定义服务器名称,nginx默认使用空字符串作为名称。

nginx直到0.8.48版本,使用的是机器的主机名作为服务器名称

如果服务器名称定义为“$hostname”(0.9.4),会使用机器的主机名。
如果请求直接用IP地址而不是主机名,请求头中的Host域就是IP地址,可以使用IP地址所谓服务器名称。

server {
    listen       80;
    server_name  example.org
                 www.example.org
                 ""
                 192.168.1.1
                 ;
    ...
}

名称设为“_”可以捕获所有的主机名:

server {
    listen       80  default_server;
    server_name  _;
    return       444;
}

这个并不是什么特殊的用法,只是使用了域名中永远不会出现的几个无效的字符。其他的无效名称如“-”、“!@#”都可以。
直到0.6.25版本,nginx支持特殊名字“*”被错误解析为全捕获名称。现在这个已经过时,使用server_name_in_redirect指令。注意,没有办法通过server_name指令定义全捕获的名称或默认名称。这是listen指令的属性而不是server_name的指令。可以定义不同的服务器分别监听*:80和*:8080,其中一个是80端口的默认服务器,另一个是8080端口的默认服务器:

server {
    listen       80;
    listen       8080  default_server;
    server_name  example.net;
    ...
}

server {
    listen       80  default_server;
    listen       8080;
    server_name  example.org;
    ...
}

优化
具体名称、*开头的通配符名称、*结尾的通配符名称分配存储在三张哈希表中并绑定不同的端口。在配置阶段会对哈希表的大小进行优化,一般提高名称在CPU缓存中的命中率。
具体名称的哈希表是最先被搜索的,如果名称没有找到,会搜索*开头的通配符哈希表,最后再搜索*结尾的通配符哈希表。
搜索通配符哈希表要比搜索具体名称的哈希表更慢,因为要搜索域名的一部分。注意,特殊形式的通配符名称“.example.org”是存储在统配符哈希表中而不是固定名称的哈希表中(虽然名称中没有*)。
正则表达式最慢,并且不可扩展。
因此,最好用固定的名称。例如,如果最常访问的名字是example.org 和www.example.org,下面的写法更高效:

server {
    listen       80;
    server_name  example.org  www.example.org  *.example.org;
    ...
}

下面的写法性能要低一些:

server {
    listen       80;
    server_name  .example.org;
    ...
}

如果定义了大量的名称,或一个名称很长,需要用到server_names_hash_max_size和server_names_hash_bucket_size指令,它们应该写在http块内。server_names_hash_bucket_size指令的值可以是32或64,也可以是其他值,取决于CPU缓存的大小。如果默认值是32,服务器名称定义为“too.long.server.name.example.org”,nginx会启动失败,并有如下报错信息:

could not build the server_names_hash,
you should increase server_names_hash_bucket_size: 32

这时,需要通过指令将这个值调整到原值的2倍:

http {
    server_names_hash_bucket_size  64;
    ...

如果定义了很多个服务器名称,会出现另一个错误:

could not build the server_names_hash,
you should increase either server_names_hash_max_size: 512
or server_names_hash_bucket_size: 32

这种情况下,首先尝试将server_names_hash_max_size的值调整到接近名称数量。当这种方法不奏效或nginx不可接受过长的数值时,尝试增加server_names_hash_bucket_size的值。
如果只配置了一个server块监听端口,nginx根本不会检测服务器名称(也不会建立哈希表)。但有一个例外,如果名称是一个有捕获的正则表达式,nginx会执行正则表达式以便获取捕获信息。
兼容性

  • 特殊的服务器名称“$hostname”从0.9.4开始支持。
  • 从0.8.48开始默认的服务器名称是空的。
  • Named regular expression server name captures have been supported since 0.8.25.
  • Regular expression server name captures have been supported since 0.7.40.
  • 空的服务器名称从0.7.12开始被支持
  • 通配符和正则表达式从0.6.25版本开始被支持
  • 正则表达式服务器名称从0.6.7版本被支持
  • 通配符example.*形式从0.6.0版本开始被支持
  • 特殊形式.example.org从0.3.18版本开始被支持
  • 统配符*.example.org形式从0.1.13版本开始被支持