Nginx负载均衡(Load Balancing)的多种方式及使用配置

负载均衡(load balancing)是优化资源利用率、最大化吞吐量、减少延迟和确保容错配置的常用技术。
简单来说就是通过一定的规则,将客户端的请求分摊到不同的服务器上来处理,达到分流的目的。

目前使用的版本:

nginx version: nginx/1.24.0

nginx.org的文档 中对于负载均衡的描述有点乱,里面写着其默认支持3种负载均衡机制。
但是实际上按照nginx.com最新文档,nginx应该是默认支持6种负载均衡方法(开源版本支持5种,商业版Nginx Plus支持6种)。
分别是:

  1. 轮询(Round Robin):请求都按照顺序以轮询的方式均匀的分发到每台服务器上。
  2. 最少连接(Least Connections):将请求分发到连接数最少的服务器上。
  3. IP-Hash:将请求的客户端IP进行hash计算,根据获取的值,分配到特定的服务器,同一个IP每次请求都会分配到相同的服务器上。
  4. 通用Hash(Generic Hash):用户可以自定义用于hash计算的值,并不局限于IP。
  5. 随机(Random):请求将被分配到随机选择的服务器。
  6. 最短延迟时间(Least Time)仅适用于Nginx Plus中。请求将选择平均延迟最低和活动连接数最少的服务器进行处理。

此外还可以通过在编译时增加nginx module来提供额外的负载均衡方法支持,如提供fair指令的ngx_http_upstream_fair_module

轮询是Nginx负载均衡的默认机制。
除了指定负载均衡方法外,还可以给每台服务器指定负载均衡的权重(weight),权重会影响负载均衡的优先级,默认权重都为1。

轮询(Round Robin)

写一个简单的用于测试的web服务,功能是请求/返回服务端口是多少,用来判断负载均衡下,请求最终到了哪台服务器上。

@RestController
public class TestController {
    @Value("${server.port}")
    private String port;
    @RequestMapping("/")
    public Object test() {
        return "Port is:" + port;
    }
}

在本机上启动了三个服务,对应的地址分别是127.0.0.1:8080127.0.0.1:8081127.0.0.1:8082
在配置文件中,定义一个服务器组myapp1,这是最简单的负载均衡配置。
将这三个服务器地址定义到服务器组myapp1中。
监听端口80,并配置反向代理指向服务器组myapp1

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log logs/access.log main;

    sendfile on;

    keepalive_timeout 65;

    gzip on;

    # 服务器组myapp1 默认的负载均衡轮询策略
    upstream myapp1 {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

    server {
        listen 80;

        location / {
            # 反向代理
            proxy_pass http://myapp1;
        }
    }

}

此时已完成最基本的负载均衡配置。
访问nginx的80端口,可以看到每次请求将按照定义的顺序分配到服务器组的三个服务器上。

最少连接(Least Connections)

启用最少连接策略,只需要在upstream块中增加一行least_conn;配置。

    upstream myapp1 {
        # 最少连接负载均衡
        least_conn;
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

轮询请求次数的分配上更均衡,意味着所有请求都将按照顺序分配。
但是实际上不同请求可能所需的耗时并不相同,如果存在长耗时的请求,轮询情况下可能会出现这种情况:“恰好长耗时请求都分配到了某(几)台服务器”。这使得这些服务器的资源消耗明显大于其他服务器,导致负载过高。
此时最少连接可能是一种更好的规则,它会将请求分配到当前连接数最少的服务器上,使当前不太繁忙(负载不太高)的服务器承担请求。
在配置相同的情况下,对于服务器负载的控制更加均衡。

IP-Hash

启用ip_hash策略,只需要在upstream块中增加一行ip_hash;配置。

    upstream myapp1 {
        # ip_hash
        ip_hash;
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

IP-Hash会对发起请求的客户端的IP进行hash key计算,根据hash值映射到某台服务器上。
因为IP进行hash计算后的值是固定的,因此同一个IP的每次请求都会分配到固定的某台服务器上。
这种方式的优点就是,同一个IP(客户端)的请求都由同一台服务器进行处理,默认情况下保持会话(Session persistence)
当请求分配到的服务不可用时,会将此服务剔除,然后请求根据hash值重新分配到一台固定的可用服务器上,当服务再次可用时,请求又会分配到最初的服务器上。
想主动暂时把某个服务从负载均衡中剔除,可以在服务最后增加参数down

    upstream myapp1 {
        ip_hash;
        server 127.0.0.1:8080;
        # 将该服务器从负载均衡中剔除
        server 127.0.0.1:8081 down;
        server 127.0.0.1:8082;
    }

比如当前访问nginx的80端口,在对IP进行计算后,分配对应的服务器是127.0.0.1:8082,多次访问仍是127.0.0.1:8082。此时将127.0.0.1:8082的服务关闭,再次访问,会发现要稍微多加载一会儿,然后请求被分配127.0.0.1:8081上了,如果8082的服务仍不可用,后续请求将一直由8081处理,直到8082恢复可用,再次由其处理。
缺点也显而易见,如果不同用户的IP进行摘要后恰好都被分配到同一(几)台服务器上,将会使这些服务器的负载过高。
现实情况中,多个客户端共享同一个公网IP的情况还是挺常见的,比如局域网内有多个用户。
需要根据实际场景来考虑使用,不太推荐直接用。

有需要保持会话需求的可以使用商业版nginx plus中的sticky指令Enabling Session Persistence
开源版nginx也有几个第三方开发的sticky模块支持,但是基本上都很久没更新了,最新版本的nginx未必能正常编译使用。

  1. nginx-sticky-module-ng
  2. nginx-sticky-module

通用Hash(Generic Hash)

IP Hash的局限性比较大,因此nginx还支持自定义值来进行hash计算,这个值可以是字符串、变量等。

    upstream myapp1 {
        # 自定义用于计算hash的值
        hash $remote_addr consistent;
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

比如以上就实现了和IP Hash相同的功能,根据客户端IP$remote_addr进行hash值计算。

随机(Random)

将请求随机分配到服务器上。

    upstream myapp1 {
        random;
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

还可以增加可选参数two来根据服务器权重随机选择两台服务器,然后根据参数值来选择其中一台服务器。

    upstream myapp1 {
        random two least_conn;
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

官方文档中的使用场景:

The Random load balancing method should be used for distributed environments where multiple load balancers are passing requests to the same set of backends. For environments where the load balancer has a full view of all requests, use other load balancing methods, such as round robin, least connections and least time.
支持的值包括:

  1. least_conn :最少活动连接数
  2. least_time=header:接收响应头的最短平均时间(仅Nginx Plus可用)
  3. least_time=last_byte:接收完整响应的最短平均时间(仅Nginx Plus可用)

加权负载均衡

负载均衡在指定基本策略之外,还能指定权重来影响平衡决策。
未配置权重(weight)的情况下,默认值为1

    upstream myapp1 {
        server 127.0.0.1:8080 weight=2;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

以上配置中,将8080服务的权重设置为2,其他服务默认权重为1。因此,访问4次nginx的80端口,请求将分配到8080两次,80818082各一次。

加权通常用在默认的轮询策略下。
但是根据官方文档,最新版本的nginx中,最少连接Hash同样可以使用权重。
测试了一下在IP-Hash下的影响。
不配置任何权重时:

    upstream myapp1 {
        ip_hash;
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

我的请求被固定分配到了8082服务。
然后在8080服务上设置weight=2

    upstream myapp1 {
        ip_hash;
        server 127.0.0.1:8080 weight=2;
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

我的请求被固定分配到了8080服务。
说明IP-HashWeight的配置均有生效,但是并未在文档中找到相关的详细说明。

根据结果猜测,应该是大概会影响hash值分配到对应服务器的权重。使IP通过一定的算法进行hash计算后,有更大的概率分配到权重更高的服务器上。

其他

  1. 配置负载均衡时候,要把对应的负载均衡指令放在upstream代码块的server指令上方。
  2. ngx_http_upstream_fair_module安装说明,功能上和最少连接(Least Connections)相似 —— 将传入请求发送到最不繁忙的后端服务器。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇