转载请备注来源: 《Nginx负载均衡搭建》 | shuwoom.com

文章摘要

本篇文章主要介绍nginx负载均衡的相关知识(包括相关指令、负载均衡常用策略)以及实践操作。

正向代理、反向代理和负载均衡的区别

正向代理

正向代理发生在客户端,是用户主动发起代理的。最长见的就是使用vpn。我们可以通过设置代理服务器访问国外网站或者公司内部网站,这个一般需要用户主动在客户端配置。

同样的,如果你想隐藏身份,就可以通过这种方式来实现,这时候目标服务器记录的并不是你的真实ip而是代理服务器的ip,当然要真正实现隐藏身份,绝不是建单配置就可以实现的,这方面的技巧大家可以在网上找,我就不展开讲。

1563614089708

反向代理

反向代理发生在服务端,从用户角度看,是不知道发生了代理行为的。

例如一个客户访问一个网站(a.com)这个网站指向的IP实际上是一个nginx反向代理,nginx反向代理将用户的请求转发到上游的服务器集群,并将返回的结果返回给用户。

对于用户来说他的访问过程如下所示:

|用户| <---> |目标服务器|

而实际的过程是:

|用户| <---> |反向代理服务器| <---> |目标服务器|
1563614136603

负载均衡

负载均衡是反向代理的一个运用。负载均衡会把大量的请求分发给上游的服务器集群,从而减轻单个服务器处理请求的压力。

1563614001845

Nginx负载均衡相关的指令

指定上游服务器地址的upstream和server指令

upstream

Syntax:upstream name { ... }
Default:
Context:http

server

Syntax:server address [parameters];
Default:
Context:upstream

重要参数如下:

  • down 表示当前的server已下线,不再服务状态
  • weight weight越大,负载的权重越大
  • backup 预留的备份机器,仅当非backup服务器不可用时,请求才会转发给backup服务器
  • max_fails 允许请求失败的次数,默认为1,当超过最大次数时,会在fail_timeout秒内这台server不允许再次被选择
  • fail_timeout 单位秒,默认值为10秒。具有两个功能:
  • 指定一段时间内,最大的失败次数max_fails
  • 到达max_fails后,该server不能访问的时间
  • max_conns server最大并发连接数,仅作用于单worker进程。默认为0,即没有限制。

Nginx负载均衡常用策略

轮询(默认)

每个请求按时间顺序逐一分配到不同的上游服务器,如果某个上游服务器宕机,则会自动剔除掉。

upstream backend {
    server 192.168.0.14;
    server 192.168.0.15;
}

权重(weight)

指定轮询的频率,weight和频率成正比,当上游服务器的性能不均匀时就可以采用这种方式。

upstream backend {
    server 192.168.0.14 weight=3;
    server 192.168.0.15 weight=7;
}

选择连接最少的上游服务器

  • 功能: web请求会被转发到上游服务器中并发连接数最少的一个。我们知道,轮询算法是把每个请求平均转发给上有服务器,是它们的负载大致相同。当时这里有个前提,就是每个服务器处理请求的时间要一致,否则有些请求占用时间较长,就会导致其所在后端的服务器负载较高。在这种场景下,把请求转发给并发数较少的服务器,能够达到更好的负载均衡效果。
  • 使用 Syntax: **least_conn**; Default: — Context: upstream
  • 例子
  upstream backend {
      least_conn;
      server 192.168.0.14
      server 192.168.0.15;
  }

基于客户端ip的hash算法(ip_hash)

  • nginx轮询和权重方式的弊端

上述两种方式有一个问题,如果http请求带登录态,及session信息,那么如上两种方法。用户在第一次登陆时,负载均衡给他分配的某一台机器会留下登陆信息,当时当用户下次访问时,由于负载均衡的轮询导致用户同一个用户可能被负载均衡分配到跟上次不同的上有服务器上,这时候该服务器没有保留登陆信息,就需要用户重新登录,从而导致登录信息丢失。

下面介绍的基于客户端ip地址hash的算法可以解决这个问题。

  • ip_hash功能 基于客户端ip地址的hash算法,以客户端的ip地址作为hash算法的关键字,映射到特定上游服务器中。

(1)对IPV4地址使用前3个字节作为关键字,对IPV6使用完整地址

(2)可以使用round-robin算法的参数

(3)可以基于realip模块修改用于执行算法的ip地址

这样每个请求按照访问的ip的hash结果分配,这样每个用户的访问都是固定到上游的某一台服务器,从而解决了session的问题。当然,关于多节点集群部署带来的session问题,还可以使用缓存技术,如redis或memcached。

upstream backend {
    ip_hash;
    server 192.168.0.14;
    server 192.168.0.15;   
}

基于任意关键字实现的hash算法

  • 功能 通过指定关键字作为hash key,基于hash算法映射到特定的上游服务器中。例如对uri做hash,则对每个uri进行hash计算得到一个数值,这个数值除以整个节点数量取余数(取模算法)。但是这个算法有一个致命的缺点,就是节点发生扩容或宕机时会导致缓存失效,我们会在下一节详细介绍。

(1)关键字可以含有变量、字符串

(2)可以使用round-robin算法的参数

例如,按请求url的hash结果来分配请求,使每个url重定向到同一个上游服务器。这样用户访问同一个uri时,会被分配到同一个服务器上,提高了缓存的命中率。

upstream backend {
    server 192.168.0.14;
    server 192.168.0.15;
    hash $request_uri;
}

Hash算法的问题

无论上面将的基于客户端IP的hash算法还是基于任意关键字的hash算法,都存在一个问题,就是当上游服务器发送宕机后扩容时,这时候机器数量发生变化,hash算法引发大量路由变更,可能导致缓存大范围失效。如下图:

加入上游服务器有5台机器

1563620636587

当发生宕机时,上游服务器只有4台,这时被除数发生变化。请求被转发的上游服务器可能发生改变,这可能导致缓存失效问题。

1563620690837

一致性hash算法

一致性hash采用除数特别大,假设有一个hash环,是个闭环,把32位二进制整数转换为十进制后均匀分布在整个环上。hash的结果是除以2的32次方。结果一定是落在环上。那么这个落在环上的点离哪个节点近,就访问在哪个节点的服务器。如下图所示:

1563621452791

当我们扩容上游服务器时,如下图增加节点5。那么这知会影响节点5周围的请求,而不会影响其他节点服务器上的请求。从而我们就解决了hash算法碰上宕机或扩容时导致缓存失效的问题。

1563621505137

使用方法如下,当我们启用后面的consistent参数时,就会启用一致性hash算法。

Syntax:hash key [consistent];
Default:
Context:upstream

例如:

upstream backend {
    server 192.168.0.14;
    server 192.168.0.15;
    hash $request_uri consistent;
}

Nginx负载均衡集群搭建实践

配置如下:

user  nginx;
pid /run/nginx.pid;
worker_processes  auto;
worker_rlimit_nofile 100000;

events {
    worker_connections  2048;
    multi_accept on;
}

http {
    sendfile on;

    access_log off;
    error_log  /data/log/nginx-1.0/error.log  error;

    server {
        listen 8001;
        server_name localhost;
        location / {
            return 200 '8001 server response.uri=>$request_uri\n';
        }

    }

    server {
        listen 8002;
        server_name localhost;
        location / {
            return 200 '8002 Server response.uri=>$request_uri\n';
        }
    }

    server {
        listen 8003;
        server_name localhost;
        location / {
            return 200 '8003 Server response.uri=>$request_uri\n';
        }
    }

    upstream backend {
        hash $request_uri consistent;
        server 127.0.0.1:8001;
        server 127.0.0.1:8002;
        server 127.0.0.1:8003;
    }

    server {
        listen 80;
        server_name localhost;

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

访问记录:

[root@VM_16_4_centos conf]# curl localhost/1
8001 server response.uri=>/1
[root@VM_16_4_centos conf]# curl localhost/2
8002 Server response.uri=>/2
[root@VM_16_4_centos conf]# curl localhost/3
8002 Server response.uri=>/3
[root@VM_16_4_centos conf]# curl localhost/4
8002 Server response.uri=>/4
[root@VM_16_4_centos conf]# curl localhost/5
8003 Server response.uri=>/5

转载请备注来源: 《Nginx负载均衡搭建》 | shuwoom.com

打赏

发表评论

电子邮件地址不会被公开。