upstream均衡负载模块(一)加权轮询策略

upstream负载均衡模块主要是用于从“upstream”定义的后端服务器中选择一台服务器进行连接。nginx先使用负载均衡模块选择一台主机,再使用upstream模块实现与这台主机的交互。

负载均衡策略

Nginx负载均衡策略主要分成两大类:内置策略和扩展策略。我们主要分析内置策略,内置策略主要是ip hash策略和加权轮询策略。默认情况下,这两种策略会被编译进内核,只需在配置时指明参数就行。扩展策略有很多,通用hash,consistent hash等,默认不编译进内核,是第三方模块。

nginx的upstream目前支持四种方式:

1)轮询(默认) 

      每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 

2)weight 

      指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 

2)ip_hash 

      每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。  

3)fair(第三方) 

      按后端服务器的响应时间来分配请求,响应时间短的优先分配。  

4)url_hash(第三方)

Nginx默认采用round_robin加权算法。如果要选择其他的负载均衡算法,必须在upstream的配置上下文中通过配置指令ip_hash明确指定

upstream load_balance{
    ip_hash;
    server localhost:8001;
    server localhost:8002;
}
指令如下
static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {
    { ngx_string("ip_hash"),
      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
      ngx_http_upstream_ip_hash,
      0,
      0,
      NULL },
 
      ngx_null_command
};


整个http配置块被解析完毕后,会调用所有http模块的对应的初始化函数 对于模块ngx_http_upstream_module而言,对应的main配置初始函数是ngx_http_upstream_init_main_conf()
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赋值的那一行代码。如果没有进行策略选择,那么就调用默认的策略初始函数ngx_http_upstream_init_round_robin,也就是加权轮询策略。否则的话就调用uscfp[i]->peer.init_upstream函数,如果选择ip hash负载策略,那么就会用ngx_http_upstream_init_ip_hash()。
在正式分析负载模块的具体代码前,我们先介绍熟悉几个相关的结构体

typedef struct {
    ngx_addr_t                      *addrs;//指向存储IP地址的数组的指针,host信息(对应的是 ngx_url_t->addrs )
    ngx_uint_t                       naddrs;//与第一个参数配合使用,数组元素个数(对应的是 ngx_url_t->naddrs )
    ngx_uint_t                       weight;
    ngx_uint_t                       max_fails;
    time_t                           fail_timeout;

    unsigned                         down:1;
    unsigned                         backup:1;
} ngx_http_upstream_server_t;

typedef struct ngx_http_upstream_srv_conf_s  ngx_http_upstream_srv_conf_t;

struct ngx_http_upstream_srv_conf_s {
    ngx_http_upstream_peer_t         peer;
    void                           **srv_conf;//在 ngx_http_upstream()函数中被设置,指向的是本层的srv_conf

    ngx_array_t                     *servers;  /*array of ngx_http_upstream_server_t */

    ngx_uint_t                       flags;//调用函数时ngx_http_upstream_add() 指定的标记
    ngx_str_t                        host;//在函数 ngx_http_upstream_add() 中设置(e.g. upstream backend中的backend)
    u_char                          *file_name;//"/usr/local/nginx/conf/nginx.conf"
    ngx_uint_t                       line;//proxy在配置文件中的行号
    in_port_t                        port;//使用的端口号(ngx_http_upstream_add()函数中添加, 指向ngx_url_t-->port,通常在函数ngx_parse_inet_url()中解析)
    in_port_t                        default_port;//默认使用的端口号(ngx_http_upstream_add()函数中添加, 指向ngx_url_t-->default_port)
    ngx_uint_t                       no_port;  /* unsigned no_port:1 */
};
typedef struct {
    //使用负载均衡的类型,默认采用 ngx_http_upstream_init_round_robin()
    ngx_http_upstream_init_pt        init_upstream;
    //使用的负载均衡类型的初始化函数
    ngx_http_upstream_init_peer_pt   init;
    //us->peer.data = peers; 指向的是 ngx_http_upstream_rr_peers_t(函数 ngx_http_upstream_init_round_robin()中设置)
    void                            *data;
} ngx_http_upstream_peer_t;

typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us);
如果upstream中服务器为空,那么默认使用proxy_pass。将利用函数ngx_inet_resolve_host依据us参数中的host和port进行解析。将结果保存在一个ngx_url_t类型的变量中:

typedef struct {
    ngx_str_t                 url;					//保存IP地址+端口信息(e.g. 192.168.124.129:8011 或 money.163.com)
    ngx_str_t                 host;					//保存IP地址信息
    ngx_str_t                 port_text;				//保存port字符串
    ngx_str_t                 uri;					//uri部分,在函数ngx_parse_inet_url()中设置

    in_port_t                 port;					//端口,e.g. listen指令中指定的端口(listen 192.168.124.129:8011)
    in_port_t                 default_port;				//默认端口(当no_port字段为真时,将默认端口赋值给port字段, 默认端口通常是80)
    int                       family;					//address family, AF_xxx

    unsigned                  listen:1;				//是否为指监听类的设置
    unsigned                  uri_part:1;
    unsigned                  no_resolve:1;				//根据情况决定是否解析域名(将域名解析到IP地址)
    unsigned                  one_addr:1;				//等于1时,仅有一个IP地址

    unsigned                  no_port:1;				//标识url中没有显示指定端口(为1时没有指定)
    unsigned                  wildcard:1;				//标识是否使用通配符(e.g. listen *:8000;)

    socklen_t                 socklen;				//sizeof(struct sockaddr_in)
    u_char                    sockaddr[NGX_SOCKADDRLEN];		//sockaddr_in结构指向它

    ngx_addr_t               *addrs;				//数组大小是naddrs字段;每个元素对应域名的IP地址信息(struct sockaddr_in),在函数中赋值(ngx_inet_resolve_host())
    ngx_uint_t                naddrs;				//url对应的IP地址个数,IP格式的地址将默认为1

    char                     *err;					//错误信息字符串
} ngx_url_t;
此函数会创建后端服务器列表,并且将非后备服务器与后备服务器分开进行各自单独的链表。每一个后端服务器用一个结构体ngx_http_upstream_rr_peer_t与之对应(ngx_http_upstream_round_robin.h):
typedef struct {
    struct sockaddr                *sockaddr;//后端服务器地址
    socklen_t                       socklen;//后端服务器地址长度
    ngx_str_t                       name;//后端名称

    ngx_int_t                       current_weight;//当前权重,nginx会在运行过程中调整此权重
    ngx_int_t                       effective_weight;
    ngx_int_t                       weight;//配置的权重

    ngx_uint_t                      fails;//已尝试失败次数
    time_t                          accessed;//检测失败时间,用于计算超时
    time_t                          checked;

    ngx_uint_t                      max_fails;//最大失败次数
    time_t                          fail_timeout;//多长时间内出现max_fails次失败便认为后端down掉了

    ngx_uint_t                      down;          /* unsigned  down:1; *///指定某后端是否挂了

#if (NGX_HTTP_SSL)
    ngx_ssl_session_t              *ssl_session;   /* local to a process */
#endif
} ngx_http_upstream_rr_peer_t;

列表最前面需要带有一些head信息,用结构体ngx_http_upstream_rr_peers_t与之对应:

typedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;

struct ngx_http_upstream_rr_peers_s {
    ngx_uint_t                      number;//队列中服务器数量

 /* ngx_mutex_t                    *mutex; */

    ngx_uint_t                      total_weight;//所有服务器总权重

    unsigned                        single:1;//为1表示后端服务器总共只有一台,用于优化,此时不需要再做选择
    unsigned                        weighted:1;//为1表示总的权重值等于服务器数量

    ngx_str_t                      *name;

    ngx_http_upstream_rr_peers_t   *next;//后备服务器列表挂载在这个字段下

    ngx_http_upstream_rr_peer_t     peer[1];
};

ngx_http_upstream_init_round_robin函数具体分析:

  1. //函数:初始化服务器负载均衡表      
  2. //参数:  
  3. //us:ngx_http_upstream_main_conf_t结构体中upstreams数组元素  
  4. ngx_int_t  
  5. ngx_http_upstream_init_round_robin(ngx_conf_t *cf,  
  6.     ngx_http_upstream_srv_conf_t *us)  
  7. {  
  8.     ngx_url_t                      u;  
  9.     ngx_uint_t                     i, j, n, w;  
  10.     ngx_http_upstream_server_t    *server;  
  11.     ngx_http_upstream_rr_peers_t  *peers, *backup;  
  12.   
  13.     //回调指针设置  
  14.     us->peer.init = ngx_http_upstream_init_round_robin_peer;  
  15.   
  16.     //服务器数组指针不为空  
  17.     if (us->servers) {  
  18.         server = us->servers->elts;  
  19.   
  20.         n = 0;  
  21.         w = 0;  
  22.   
  23.     //遍历所有服务器  
  24.         for (i = 0; i < us->servers->nelts; i++) {  
  25.         //是后备服务器,跳过  
  26.             if (server[i].backup) {  
  27.                 continue;  
  28.             }  
  29.   
  30.         //服务器地址数量统计  
  31.             n += server[i].naddrs;  
  32.         //总的权重计算  
  33.             w += server[i].naddrs * server[i].weight;  
  34.         }  
  35.   
  36.         if (n == 0) {  
  37.             ngx_log_error(NGX_LOG_EMERG, cf->log, 0,  
  38.                           "no servers in upstream \"%V\" in %s:%ui",  
  39.                           &us->host, us->file_name, us->line);  
  40.             return NGX_ERROR;  
  41.         }  
  42.   
  43.     //为非后备服务器分配空间  
  44.         peers = ngx_pcalloc(cf->pool, 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值