TrumanWong

Nginx负载均衡策略

TrumanWong
6/24/2024

负载均衡技术是将大量的客户端请求通过特定的策略分配到集群中的节点,实现快速响应的应用技术。在应对高并发的应用请求时,单节点的应用服务计算能力有限,无法满足客户端的响应需求,通过负载均衡技术,可以将请求分配到集群中的多个节点中,让多个节点分担高并发请求的运算,快速完成客户端的请求响应。

Nginx支持多种负载均衡策略,如轮询Round Robin、一致性哈希Consistent Hash、IP哈希IP Hash、最少连接least_conn等。Nginx的默认负载均衡策略为轮询策略,不需要配置指令,轮询策略通过server的权重参数可实现手动分配的加权轮询策略。负载均衡策略配置指令均应编辑在upstream指令域的最上方。

Nginx负载均衡示例配置:

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

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

server语法:server address [parameters]

address可以指定为域名,也可以使用ip地址+端口形式,如果不指定端口,则默认使用80端口,或者使用unix套接字路径。

server指定可以定义以下参数:

  • weight=number:设置服务器的权重,默认为1。
  • max_conns=number:限制代理服务器的最大number同时活动连接数1.11.5。默认值为零,表示没有限制。如果服务器组不驻留在共享内存中,则限制按每个工作进程进行。
  • max_fails=number:设置允许请求代理服务器失败的次数 。默认为 1。0表示禁用尝试计数。
  • fail_timeout=time:设置经过max_fails失败后,服务暂停的时间,默认是10秒。
  • backup:将服务标记为备用服务。当主服务不可用时,它将被启用。该参数不能与haship_hashrandome负载均衡方法一起使用
  • down:将服务标记为永久不可用。

轮询

轮询Round Robin策略是Nginx配置中默认的负载均衡策略,该策略将客户端的请求依次分配给后端的服务器节点,对后端集群中的服务器实现轮流分配。轮询策略绝对均衡,且实现简单,但也会因后端服务器处理能力的不同而影响整个集群的处理性能。

加权轮询

Nginx的轮询策略中,为了避免因集群中服务器性能的差异对整个集群性能造成影响,在轮询策略的基础上增加了权重参数,让使用者可以手动根据集群中各服务器的性能将请求数量按照权重比例分配给不同的被代理服务器。

平滑轮询

在加权轮询策略中,会按照权重的高低分配客户端请求,若按照高权重分配完再进行低权重分配的话,可能会出现的情况是高权重的服务器一直处于繁忙状态,压力相对集中。Nginx通过平滑轮询算法,使得上游服务器组中的每台服务器在总权重比例分配不变的情况下,均能参与客户端请求的处理,有效避免了在一段时间内集中将请求都分配给高权重服务器的情况发生。

示例如下:

http {
    upstream backend {
        server backend1.example.com weight=5;
        server backend2.example.com weight=1;
        server backend3.example.com weight=1;
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

配置样例中Nginx平滑轮询策略计算过程如下:

  • 当前配置中backend1.example.com,backend2.example.com,backend3.example.com服务器的配置权重为{5,1,1}

  • 计算过程如下表所示:

    轮询次数 当前权重 选择后权重 选择节点
    0 {0,0,0} {0,0,0}
    1 {5,1,1} {-2,1,1} backend1.example.com
    2 {3,2,2} {-4,2,2} backend1.example.com
    3 {1,3,3} {1,-4,3} backend2.example.com
    4 {6,-3,4} {-1,-3,4} backend1.example.com
    5 {4,-2,5} {4,-2,-2} backend3.example.com
    6 {9,-1,-1} {2,-1,-1} backend1.example.com
    7 {7,0,0} {0,0,0} backend1.example.com
  • 有效权重effective_weight:初始值为配置文件中权重的值,会因节点的健康状态而变化。

  • 当前权重current_weight:节点被选择前的权重值,由上一个选择后权重值及各节点与自己的有效权重值相加而得。

  • 选择后权重:所有节点中权重最高节点的当前权重值为其初始值与有效总权重相减的值,其他节点的权重值不变。

  • 有效总权重为所有节点中非备份、非失败状态的服务器的有效权重之和。

  • 根据上述平滑轮询算法,选择节点顺序为{backend1.example.com,backend1.example.com,backend2.example.com,backend1.example.com,backend3.example.com,backend1.example.com,backend1.example.com}

一致性哈希

配置语法:hash key [consistent]

Nginx启用哈希的负载均衡策略,是用hash指令来设置的。哈希策略方法可以针对客户端访问的URL计算哈希值,对相同的URL请求,Nginx可以因相同的哈希值而将其分配到同一后端服务器。当后端服务器为缓存服务器时,将极大提高命中率,提升访问速度。

优点

可以使不同客户端的相似请求发送给同一被代理服务器,当被代理服务器为缓存服务器场景应用时,可以极大提高缓存的命中率。

缺点

当上游服务器组中的节点数量发生变化时,将导致所有绑定被代理服务器的哈希值重新计算,影响整个集群的绑定关系,产生大量回源请求。

针对哈希算法的缺点,Nginx提供了consistent参数启用一致性哈希Consistent Hash负载均衡策略。Nginx采用的是Ketama一致性哈希算法,使用一致性哈希策略后,当上游服务器组中的服务器数量变化时,只会影响少部分客户端的请求,不会产生大量回源。

Nginx一致性哈希计算过程如下:

  • 根据配置文件中非备份状态服务器的总权重乘以160计算出总的虚拟节点数量,初始化虚拟节点数组。
  • 遍历轮询策略中的被代理服务器列表,根据每个服务器的权重数乘以160得出该服务器的虚拟节点数量,并根据服务器的HOSTPORT计算出该服务器的基本哈希base_hash
  • 循环每个服务器虚拟节点总数次数,由基本哈希base_hash值与上一个虚拟节点的哈希值PREV_HASH依次计算出所有属于该服务器的虚拟节点哈希值,并把虚拟节点哈希值与服务器映射关系保存在虚拟节点哈希值数组中。
  • 对虚拟节点哈希值数组进行排序去重处理,得到新的有效虚拟节点哈希值数组。

配置示例如下:

http {
    upstream backend {
        hash $request_uri consistent;
        server backend1.example.com weight=5;
        server backend2.example.com weight=1;
        server backend3.example.com weight=1;
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

计算过程如下:

  • 首先根据$request_uri计算哈希值。
  • 通过二分法,快速在虚拟节点列表中选出该哈希值所在范围的最大虚拟节点哈希值。
  • 通过虚拟节点哈希值与虚拟节点集合总数取余,获得对应的服务器作为备选服务器。
  • 遍历轮询策略中被代理服务器列表,判断备选服务器的有效性,选出服务器。
  • 如果循环20次仍无法选出,则使用轮询策略进行选择

IP哈希

IP哈希IP Hash负载均衡策略根据客户端IP计算出哈希值,然后把请求分配给该数值对应的被代理服务器。在哈希值不变且被代理服务器可用的前提下,同一客户端的请求始终会被分配到同一台被代理服务器上。IP哈希负载均衡策略常被应用在会话Session保持的场景。

HTTP客户端在与服务端交互时,因为HTTP协议是无状态的,所以任何需要上下文逻辑的情景都必须使用会话保持机制,会话保持机制是通过客户端存储由唯一的Session ID进行标识的会话信息,每次与服务器交互时都会将会话信息提交给服务端,服务端依照会话信息实现客户端请求上下文的逻辑关联。会话信息通常存储在被代理服务器的内存中,如果负载均衡将客户端的会话请求分配给其他被代理服务器,则该会话逻辑将因为会话信息失效而中断。所以为确保会话不中断,需要负载均衡将同一客户端的会话请求始终都发送到同一台被代理服务器,通过会话保持实现会话信息的有效传递。

配置示例如下:

http {
    upstream backend {
        ip_hash;
        server backend1.example.com weight=5;
        server backend2.example.com weight=1;
        server backend3.example.com weight=1;
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

计算过程如下:

  • 在多层代理的场景下,请确保当前Nginx可获得真实的客户端源IP。
  • 首先会根据客户端的IPv4地址的前三个八位字节或整个IPv6地址作为哈希键计算哈希值。
  • 根据哈希值与配置文件中非备份状态服务器的总权重计算出哈希余数。
  • 按照轮询策略选出初始被代理服务器,如果哈希余数大于初始被代理服务器的权重,则遍历轮询策略中被代理服务器列表,否则初始被代理服务器将被选出。
  • 当遍历轮询策略中被代理服务器列表时,要用哈希余数依次减去轮询策略中的上一个被代理服务器的权重,直到哈希余数小于某个被代理服务器的权重时该被代理服务器被选出。
  • 如果循环20次依然无法选出,则使用轮询策略进行选择

最少连接

默认配置下轮询算法是把客户端的请求平均分配给每个被代理服务器,每个被代理服务器的负载大致相同,该场景有个前提就是每个被代理服务器的请求处理能力是相当的。如果集群中某个服务器处理请求的时间比较长,那么该服务器的负载也相对增高。在最少连接least_conn负载均衡策略下,会在上游服务器组中各服务器权重的前提下将客户端请求分配给活跃连接最少的被代理服务器,进而有效提高处理性能高的被代理服务器的使用率。

http {
    upstream backend {
        least_conn;
        server backend1.example.com weight=4;
        server backend2.example.com weight=2;
        server backend3.example.com weight=1;
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

计算过程如下:

  • 遍历轮询策略中被代理服务器列表,比较各个后端的活跃连接数conns与其权重weight的比值,选取比值最小者分配客户端请求。
  • 如果上一次选择了backend1服务器,则当前请求将在backend2backend3服务器中选择。
  • backend2的活跃连接数为100,backend3的活跃连接数为60,则backend2的比值conns/weight为50,backend3的比值conns/weight为60,因此当前请求将分配给backend2

随机负载算法

语法:random [two [method]];

Nginx集群环境下,每个Nginx均通过自身对上游服务器的了解情况进行负载均衡处理,这种场景下,很容易出现多台Nginx同时把请求都分配给同一台被代理服务器的场景,该场景被称为羊群行为Herd BehaviorNginx基于两种选择的力量Power of Two Choices原理,设计了随机Random负载算法。该算法使Nginx不再基于片面的情况了解使用固有的负载均衡策略进行被代理服务器的选择,而是随机选择两个,在经过比较后进行最终的选择。随机负载算法提供了一个参数two,当这个参数被指定时,Nginx会在考虑权重的前提下,随机选择两台服务器,然后用以下几种方法选择一个服务器:

  • 最少连接数,配置指令为least_conn,默认配置。
  • 响应头最短平均时间,配置指令为least_time=header,仅对Nginx Plus版本有效。
  • 完整请求最短平均时间,配置指令为least_time=last_byte,仅对Nginx Plus版本有效。

配置示例如下:

http {
    upstream backend {
        random two least_conn;
        server backend1.example.com;
        server backend2.example.com;
        server backend3.example.com;
        server backend4.example.com;
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

在只有单台Nginx服务器时,一般不建议使用随机负载算法。