nginx ip_hash 算法的实现

感觉nginx的 ip hash 每次hash后的值都变化了啊,不会总是路由到一台机器,这里的根据ip hash 岂不是没有意义了?


/*
 * Copyright (C) Igor Sysoev
 */




#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>




typedef struct 
{
    /* the round robin data must be first */


    ngx_http_upstream_rr_peer_data_t   rrp;


    ngx_uint_t                         hash;


    u_char                             addr[3];


    u_char                             tries;


    ngx_event_get_peer_pt              get_rr_peer;


} ngx_http_upstream_ip_hash_peer_data_t;




static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us);
static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
    void *data);
static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf);




//设定根据配置文件初始化的函数为 ngx_http_upstream_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_upstream_ip_hash,
      0,
      0,
      NULL },


      ngx_null_command
};




static ngx_http_module_t  ngx_http_upstream_ip_hash_module_ctx = 
{
    NULL,                                  /* preconfiguration */
    NULL,                                  /* postconfiguration */


    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */


    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */


    NULL,                                  /* create location configuration */
    NULL                                   /* merge location configuration */
};




ngx_module_t  ngx_http_upstream_ip_hash_module = 
{
    NGX_MODULE_V1,
    &ngx_http_upstream_ip_hash_module_ctx, /* module context */
    ngx_http_upstream_ip_hash_commands,    /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};




/*






*/


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;
    }


//不同的部分,每种后端选择算法,都使用了不同的结构体,这些结构体的初始化函数为peer.init

//这里设定peer的初始化函数为ngx_http_upstream_init_ip_hash_peer


    us->peer.init = ngx_http_upstream_init_ip_hash_peer;


    return NGX_OK;
}




static ngx_int_t
ngx_http_upstream_init_ip_hash_peer( ngx_http_request_t *r,
    ngx_http_upstream_srv_conf_t *us )
{
    u_char                                 *p;
    struct sockaddr_in                     *sin;
    ngx_http_upstream_ip_hash_peer_data_t  *iphp;


    iphp = ngx_palloc( r->pool, sizeof ( ngx_http_upstream_ip_hash_peer_data_t ) );


    if ( iphp == NULL ) 
{
        return NGX_ERROR;
    }


    r->upstream->peer.data = &iphp->rrp;


//公共的peer初始化函数初始化下


    if ( ngx_http_upstream_init_round_robin_peer( r, us ) != NGX_OK)
{
        return NGX_ERROR;
    }


//这里的选择算法为ngx_http_upstream_get_ip_hash_peer


    r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;


    /* AF_INET only */


    if ( r->connection->sockaddr->sa_family == AF_INET ) 
{
        sin = (struct sockaddr_in *) r->connection->sockaddr;


        p = (u_char *) &sin->sin_addr.s_addr;
        iphp->addr[0] = p[0];
        iphp->addr[1] = p[1];
        iphp->addr[2] = p[2];


    } 
else
{
        iphp->addr[0] = 0;
        iphp->addr[1] = 0;
        iphp->addr[2] = 0;
    }


    iphp->hash = 89;
    iphp->tries = 0;


    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;


    return NGX_OK;
}




static ngx_int_t
ngx_http_upstream_get_ip_hash_peer( ngx_peer_connection_t *pc, void *data )
{
    ngx_http_upstream_ip_hash_peer_data_t  *iphp = data;


    time_t                        now;
    uintptr_t                     m;
    ngx_uint_t                    i, n, p, hash;
    ngx_http_upstream_rr_peer_t  *peer;


    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
                   "get ip hash peer, try: %ui", pc->tries);


    /* TODO: cached */


//失败尝试次数 iphp->tries超过20了,则用round robin来选择一个,容错


    if ( iphp->tries > 20 || iphp->rrp.peers->single )
{
        return iphp->get_rr_peer( pc,  &iphp->rrp );
    }


    now = ngx_time();


    pc->cached = 0;
    pc->connection = NULL;


//hash初始化为 iphp->hash,每次循环后都变更


    hash = iphp->hash;


    for ( ; ; )
{


//根据上次循环计算得到的hash值再次hash,根据这个hash值hash到一个下标


        for ( i = 0; i < 3; i++ )
{
            hash = ( hash * 113 + iphp->addr[i]) % 6271;
        }


        p = hash % iphp->rrp.peers->number;


        n = p / ( 8 * sizeof ( uintptr_t ) );


        m = (uintptr_t) 1 << p % ( 8 * sizeof ( uintptr_t ) );


        if ( !( iphp->rrp.tried[ n ] & m ) )
{
// 还没有尝试过这个backend


            ngx_log_debug2( NGX_LOG_DEBUG_HTTP, pc->log, 0,
                           "get ip hash peer, hash: %ui %04XA", p, m );


            peer = &iphp->rrp.peers->peer[p];


            /* ngx_lock_mutex(iphp->rrp.peers->mutex); */


            if ( !peer->down )
{
//没有挂掉,ok的了,返回这个backend


                if ( peer->max_fails == 0 || peer->fails < peer->max_fails )
{
                    break;
                }


                if ( now - peer->accessed > peer->fail_timeout )
{
                    peer->fails = 0;


                    break;
                }
            }


//挂掉了,则设置尝试数组,tries减少1


            iphp->rrp.tried[n] |= m;


            /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */


            pc->tries--;
        }


//递增失败尝试次数超过20了,则用round robin选择一个


//没超过20,则接着尝试


        if ( ++iphp->tries >= 20 )
{
            return iphp->get_rr_peer(pc, &iphp->rrp);
        }
    }


// ok了才会走到这里


    iphp->rrp.current = p;


    pc->sockaddr = peer->sockaddr;
    pc->socklen = peer->socklen;
    pc->name = &peer->name;


    /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */


//设置尝试过了,然后设置hash值,get_rr_peer会自动清0 tried数组


    iphp->rrp.tried[ n ] |= m;


    iphp->hash = hash;


    return NGX_OK;
}




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);


//设定peer的init_upstream函数为ngx_http_upstream_init_ip_hash


    uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;


    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                  |NGX_HTTP_UPSTREAM_MAX_FAILS
                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                  |NGX_HTTP_UPSTREAM_DOWN;


    return NGX_CONF_OK;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
`ip_hash`是Nginx的一种负载均衡算法,它基于客户端的IP地址来确定将请求转发给哪个后端服务器。使用`ip_hash`算法,相同IP地址的客户端将始终被分配到同一个后端服务器,这有助于保持会话的一致性。 要配置Nginx使用`ip_hash`算法进行负载均衡,可以按照以下步骤进行: 1. 打开Nginx的配置文件。通常在Linux系统中,配置文件位于`/etc/nginx/nginx.conf`或`/etc/nginx/conf.d/default.conf`。 2. 在`http`块内添加一个`upstream`块,定义要进行负载均衡的服务器列表。示例代码如下: ```nginx http { upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; server backend3.example.com; } ... } ``` 在上面的示例中,我们使用`ip_hash`指令在`upstream`块中启用了IP哈希负载均衡算法,并列出了要进行负载均衡的服务器。 3. 在Nginx的配置文件中,找到你要使用负载均衡的位置(如`location`块),并将其代理到上面定义的`backend`服务器组。示例代码如下: ```nginx http { upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; server backend3.example.com; } server { ... location / { proxy_pass http://backend; } ... } } ``` 在上面的示例中,我们使用`proxy_pass`将请求代理到名为`backend`的服务器组。 4. 保存配置文件并重新加载Nginx配置。在终端中执行以下命令: ```shell sudo nginx -t # 检查配置文件语法是否正确 sudo systemctl reload nginx # 重新加载Nginx配置 ``` 这样配置后,Nginx将使用`ip_hash`算法将请求分发给后端服务器。相同IP地址的客户端将被分配到同一个后端服务器上,从而保持会话的一致性。 请注意,`ip_hash`算法适用于基于客户端IP地址的负载均衡,并且要求Nginx编译时启用了`--with-http_upstream_ip_hash_module`模块。确保你的Nginx版本支持此功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值