Nginx学习—— 负载均衡模块

Posted 丶星下灯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx学习—— 负载均衡模块相关的知识,希望对你有一定的参考价值。

文章目录

nginx负载均衡模块

负载均衡模块用于从”upstream”指令定义的后端主机列表中选取一台主机。nginx先使用负载均衡模块找到一台主机,再使用upstream模块实现与这台主机的交互。

负载均衡配置

要了解负载均衡模块的开发方法,首先需要了解负载均衡模块的使用方法。因为负载均衡模块与之前提到的模块差别比较大,所以我们从配置入手比较容易理解。

  • 在nginx.conf配置文件中我们如果需要使用ip hash的负载均衡算法。我们需要写一个类似下面的配置:(负载均衡的简单配置可参考https://blog.csdn.net/Stars____/article/details/129381510?spm=1001.2014.3001.5501)
    upstream test 
    	ip_hash;
    	server 192.168.0.1;
    	server 192.168.0.2;
    
    
    从配置我们可以看出负载均衡模块的使用场景:
    1. 核心指令”ip_hash”只能在upstream 中使用。这条指令用于通知nginx使用ip hash负载均衡算法。如果没加这条指令,nginx会使用默认的round robin负载均衡模块。
    2. upstream 中的指令可能出现在”server”指令前,可能出现在”server”指令后,也可能出现在两条”server”指令之间。
      • 两者有什么区别呢:看一下下面这条配置
        upstream test 
        	server 192.168.0.1 weight=5;
        	ip_hash;
        	server 192.168.0.2 weight=7;
        
        
        此时报错:nginx: [emerg] invalid parameter "weight=7"
        可见ip_hash指令的确能影响到配置的解析的。

指令

看一下ip_hash指令的定义:

static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = 

     ngx_string("ip_hash"),
      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,	// NGX_HTTP_UPS_CONF。这个属性表示该指令的适用范围是upstream
      ngx_http_upstream_ip_hash,
      0,
      0,
      NULL ,

      ngx_null_command
;

钩子

  • ngx_http_upstream_ip_hash函数代码如下:

    static char *
    ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    
        ngx_http_upstream_srv_conf_t  *uscf;
    
        uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    
        if (uscf->peer.init_upstream) 
            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                               "load balancing method redefined");
        
    
        uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
    
        uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                      |NGX_HTTP_UPSTREAM_WEIGHT
                      |NGX_HTTP_UPSTREAM_MAX_CONNS
                      |NGX_HTTP_UPSTREAM_MAX_FAILS
                      |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                      |NGX_HTTP_UPSTREAM_DOWN;
    
        return NGX_CONF_OK;
    
    

    这段代码中有两点值得我们注意。一个是uscf->flags的设置,另一个是设置init_upstream回调。

    • 设置uscf->flags
      1. NGX_HTTP_UPSTREAM_CREATE:创建标志,如果含有创建标志的话,nginx会检查重复创建,以及必要参数是否填写。
      2. NGX_HTTP_UPSTREAM_MAX_FAILS:可以在server中使用max_fails属性。
      3. NGX_HTTP_UPSTREAM_FAIL_TIMEOUT:可以在server中使用fail_timeout属性。
      4. NGX_HTTP_UPSTREAM_DOWN:可以在server中使用down属性。
      5. NGX_HTTP_UPSTREAM_WEIGHT:可以在server中使用weight属性。
      6. NGX_HTTP_UPSTREAM_BACKUP:可以在server中使用backup属性。

    联想到刚刚遇到的那个神奇的配置错误,可以得出一个结论:在负载均衡模块的指令处理函数中可以设置并修改upstream中”server”指令支持的属性。

    这是一个很重要的性质,因为不同的负载均衡模块对各种属性的支持情况都是不一样的,那么就需要在解析配置文件的时候检测出是否使用了不支持的负载均衡属性并给出错误提示,这对于提升系统维护性是很有意义的。(也就是说,当解析到upstream中的ip_hash指令时检测是否使用了不支持的属性配置)

    但是,这种机制也存在缺陷,正如前面的例子所示,没有机制能够追加检查在更新支持属性之前已经配置了不支持属性的”server”指令。(好比ip_hash之前已经配置的weight=5的那一条server指令,是不能被ip_hash的钩子函数检查的)

    • 设置init_upstream回调
      nginx初始化upstream时,会在ngx_http_upstream_init_main_conf函数中调用设置好的初始化负载均衡模块的回调函数。
      umcf->upstreams数组中的每个元素对应upstream中的信息。
      uscfp = umcf->upstreams.elts;
      
      for (i = 0; i < umcf->upstreams.nelts; i++) 
      
          init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
                                              ngx_http_upstream_init_round_robin;
      
          if (init(cf, uscfp[i]) != NGX_OK) 
              return NGX_CONF_ERROR;
          
      
      

初始化配置

init_upstream回调函数执行时需要初始化负载均衡模块的配置,还要设置一个新钩子,这个钩子函数会在nginx处理每个请求
时作为初始化函数调用。

先看ip_hash模块初始化配置的代码:

static ngx_int_t
ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)

    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) 
        return NGX_ERROR;
    

    us->peer.init = ngx_http_upstream_init_ip_hash_peer;

    return NGX_OK;

这段代码中 ip_hash 模块首先调用另一个负载均衡模块Round Robin的初始化函数,然后再设置自己的处理请求阶段初始化钩子。

实际上几个负载均衡模块可以组成一条链表,每次都是从链首的模块开始进行处理。如果模块决定不处理,可以将处理权交给链表中的下一个模块。

这里,ip_hash模块指定Round Robin模块作为自己的后继负载均衡模块,所以在自己的初始化配置函数中也对Round Robin模块进行初始化。

初始化请求

nginx收到一个请求以后,如果发现需要访问upstream,就会执行对应的peer.init函数。这是在初始化配置时设置的回调函数。这个函数最重要的作用是构造一张表,当前请求可以使用的upstream服务器被依次添加到这张表中。之所以需要这张表,最重要的原因是如果upstream服务器出现异常,不能提供服务时,可以从这张表中取得其他服务器进行重试操作。此外,这张表也可以用于负载均衡的计算。之所以构造这张表的行为放在这里而不是在前面初始化配置的阶段,是因为upstream需要为每一个请求提供独立隔离的环境。

ngx_http_upstream_init_ip_hash_peer 函数中部分代码如下:

// 设置数据指针,这个指针就是指向前面提到的那张表。
r->upstream->peer.data = &iphp->rrp;	

// 是调用Round Robin模块的回调函数对该模块进行请求初始化。
// 前面已经提到,一个负载均衡模块可以调用其他负载均衡模块以提供功能的补充。
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) 
    return NGX_ERROR;


/*设置一个新的回调函数get。该函数负责从表中取出某个服务器。
除了get回调函数,还有另一个r->upstream->peer.free的回调函数。
该函数在upstream请求完成后调用,负责做一些善后工作。
比如我们需要维护一个upstream服务器访问计数器,那么可以在get函数中对其加1,在free中对其减1
如果是SSL的话,nginx还提供两个回调函数peer.set_session和peer.save_session*/
r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;

peer.get和peer.free回调函数

这两个函数是负载均衡模块最底层的函数,负责实际获取一个连接和回收一个连接的预备操作。之所以说是预备操作,是因为在这两个函数中,并不实际进行建立连接或者释放连接的动作,而只是执行获取连接的地址或维护连接状态的操作。需要理解的清楚一点,在peer.get函数中获取连接的地址信息,并不代表这时连接一定没有被建立,相反的,通过get函数的返回值,nginx可以了解是否存在可用连接,连接是否已经建立。这些返回值总结如下:

返回值说明nginx后续动作
NGX_DONE得到了连接地址信息,并且连接已经建立直接使用连接,发送数据
NGX_OK得到了连接地址信息,但连接并未建立建立连接,如连接不能立即建立,设置事件, 暂停执行本请求,执行别的请求
NGX_BUSY所有连接均不可用返回502错误至客户端

可能的疑问

  1. 什么时候连接是已经建立的?
    使用后端keepalive连接的时候,连接在使用完以后并不关闭,而是存放在一个队列中,新的请求只需要从队列中取出连接,这些连接都是已经准备好的。
  2. 什么叫所有连接均不可用?
    初始化请求的过程中,建立了一张表,get函数负责每次从这张表中不重复的取出一个连接,当无法从表中取得一个新的连接时,即所有连接均不可用
  3. 对于一个请求,peer.get函数可能被调用多次么?
    当某次peer.get函数得到的连接地址连接不上,或者请求对应的服务器得到异常响应,nginx会执行ngx_http_upstream_next,然后可能再次调用peer.get函数尝试别的连接
  4. upstream整体流程如下

小结

以上内容是负载均衡模块的基本组成。

负载均衡模块的配置区集中在upstream块中。

负载均衡模块的回调函数体系是以init_upstream为起点,经历init_peer,最终到达peer.get和peer.free。

其中init_peer负责建立每个请求使用的server列表,peer.get负责从server列表中选择某个server(一般是不重复选择),而peer.free负责server释放前的资源释放工作。

linux学习:Nginx--负载均衡-05

Nginx通过upstream模块来实现简单的负载均衡

 在upstream块内,定义一个服务器列表,默认的方式是轮询,如果要确定同一个访问者发出的请求总是由一个后端服务器来处理,可以设置ip_hash,

 如:

    upstream cctest1.com {
        ip_hash;
        server 127.0.0.1:9080 weight=5;
        server 127.0.0.1:8080 weight=5;
        server 127.0.0.1:1111;
    }
    
    server {
      location / {
        proxy_pass  http://cctest1.com;
      }
    }

注意:这个方法本质还是轮询,而且由于客户端的ip可能不断变化的,比如动态ip,代理,翻墙等等,因此ip_hash并不能完全保证同一个客户端总是由同

    一个服务器来处理。

指令

ip_hash

这个指令将基于客户端连接的IP地址来分发请求。哈希的关键字是客户端的C类网络地址,这个功能将保证这个客户端请求总是被转发到一台服务器上,但是如果这台服务器不可用,那么请求将转发到另外的服务器上,这将保证某个客户端有很大概率总是链接到一台服务器。

无法将权重(weght)与ip_hash联合使用来分发链接。如果有某台服务器不可用,你必须标记其为“down”,如下:

upstream backend {
  ip_hash;
  server   backend1.example.com;
  server   backend2.example.com;
  server   backend3.example.com  down;
  server   backend4.example.com;
}

注意:

 1、保证同一个客户端的请求,转发到同一台服务器处理,保证了session的一致。(对于客户端通过代理、翻墙等的方式时无效)

 2、这里并不能保证真正的负载均衡

 3、对于同一个客户端的请求,转发到同一台服务器处理时,如果这台服务器出现故障,将无法转发,出现错误。

server

指定后端服务器的名称和一些参数,可以使用域名、IP、端口、或者unix socket

语法:server name [para]

参数:

 weight = NUMBER 

  设置服务器的权重,默认为1

 max_fails = NUMBER

  在一定时间内(这个时间爱fail_timeout参数中设置)检查这个服务器是否可用时产生的最多失败请求数,默认为1,将其设置为0可以

  关闭检查,这些错误在proxy_next_upstaream或fastcgi_next_uppstaream(404错误不会是max_fails增加)中定义。

 fail_timeout = TIME

  在这个时间内产生了max_fails所设置大小的失败尝试连接请求后这个服务器不可用,同样它指定了服务器不可用的时间(在下一次尝

  试连接请求发起之前),默认为10秒,fial_timeout与前端响应时间没有直接关系,不可以使用proxy_connect_timeout和

  proxy_read_timeou来控制

 down 

  标记服务器处于离线状态,通常和ip_hash一起使用。

 backup

  如果所有的非备份服务器都宕机或繁忙,则使用本服务器(无法和ip_hash指令搭配使用)。

 $upstream_status 

  前端服务器的响应状态

 $upstream_response_time

  前端服务器的应答时间,精确到毫秒,不同的应答以逗号和冒号分开

 $upstream_http_$HEADER

  随意的HTTP协议头,如:

      $upstream_http_host

应用示例,如:

upstream xiaoliu.com {
    server 127.0.0.1:8080 weight=5;
    server 127.0.0.1:9080 weight=10;
}

server {
    listen    8888;
    server_name testserver1;
    #access_log logs/host.access.log main;
    index index.html index.jsp;
    root /opt/tomcat/webapps/ROOT;

    location ~* ^/customermger/.*\.(jpg|jpeg|gif|png|swf|ico)$ {
        root /opt/tomcat/webapps;
    }

    location ~* ^/customermger/.*\.(html|htm|js|css)$ {
        root /opt/tomcat/webapps;
    }

    location ~* ^/liuy-manager-web/.*\.(jpg|jpeg|gif|png|swf|ico)$ {
        root /opt/tomcat/webapps;
    }

   location ~* ^/liuy-manager-web/.*\.(html|htm|js|css)$ {
        root /opt/tomcat/webapps;
   }


   location ~* .*\.(jpg|jpeg|gif|png|swf|ico)$ {
        if (-f $request_filename) {
            #expires 15d;
            break;
        }
   }

   location ~* .*\.(html|htm|js|css)$ {
        #expires 1d;
   }


   location / {
      proxy_pass http://xiaoliu.com;
   }
}

第1次请求http://192.168.91.6:8888/liuy-manager-web   

    技术分享

第n次请求http://192.168.91.6:8888/liuy-manager-web 

    技术分享


Geo和GeIP模块

这两个模块主要用于做全局的负载均衡,可以根据不同的客户端IP来访问不同的服务器,如下:

http{
    geo $geo{
        default default;
        202.103.10.1/24 A;
        179.9.0.3/24 B; 
    }
    upstream default.server{
        server 192.168.0.100; 
    }
    upstream A.server{
        server 192.168.0.101; 
    }
    upstream B.server{
        server 192.168.0.102; 
    }
    server{
        listen 80;
        location / {
            proxy_pass http://$geo.server$request_uri;
        }
    }
}

用于如:北京的客户端转向北京的服务器、上海的客户端转向上海的服务

本文出自 “我爱大金子” 博客,请务必保留此出处http://1754966750.blog.51cto.com/7455444/1912805

以上是关于Nginx学习—— 负载均衡模块的主要内容,如果未能解决你的问题,请参考以下文章

Nginx学习系列之轮询的负载均衡及Ip_hash等常用指令介绍

linux学习:Nginx--负载均衡-05

2018-3-16 Linux学习笔记

Nginx 负载均衡学习

[转帖]利用nginx实现负载均衡 | 哈希算法,sticky模块实现session粘滞

Nginx学习笔记06负载均衡之负载均衡介绍