src/http/modules/ngx_http_upstream_ip_hash_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. typedef struct {
  9.     /* the round robin data must be first */
  10.     ngx_http_upstream_rr_peer_data_t   rrp;

  11.     ngx_uint_t                         hash;

  12.     u_char                             addrlen;
  13.     u_char                            *addr;

  14.     u_char                             tries;

  15.     ngx_event_get_peer_pt              get_rr_peer;
  16. } ngx_http_upstream_ip_hash_peer_data_t;


  17. static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
  18.     ngx_http_upstream_srv_conf_t *us);
  19. static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
  20.     void *data);
  21. static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
  22.     void *conf);


  23. static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {

  24.     { ngx_string("ip_hash"),
  25.       NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
  26.       ngx_http_upstream_ip_hash,
  27.       0,
  28.       0,
  29.       NULL },

  30.       ngx_null_command
  31. };


  32. static ngx_http_module_t  ngx_http_upstream_ip_hash_module_ctx = {
  33.     NULL,                                  /* preconfiguration */
  34.     NULL,                                  /* postconfiguration */

  35.     NULL,                                  /* create main configuration */
  36.     NULL,                                  /* init main configuration */

  37.     NULL,                                  /* create server configuration */
  38.     NULL,                                  /* merge server configuration */

  39.     NULL,                                  /* create location configuration */
  40.     NULL                                   /* merge location configuration */
  41. };


  42. ngx_module_t  ngx_http_upstream_ip_hash_module = {
  43.     NGX_MODULE_V1,
  44.     &ngx_http_upstream_ip_hash_module_ctx, /* module context */
  45.     ngx_http_upstream_ip_hash_commands,    /* module directives */
  46.     NGX_HTTP_MODULE,                       /* module type */
  47.     NULL,                                  /* init master */
  48.     NULL,                                  /* init module */
  49.     NULL,                                  /* init process */
  50.     NULL,                                  /* init thread */
  51.     NULL,                                  /* exit thread */
  52.     NULL,                                  /* exit process */
  53.     NULL,                                  /* exit master */
  54.     NGX_MODULE_V1_PADDING
  55. };


  56. static u_char ngx_http_upstream_ip_hash_pseudo_addr[3];


  57. static ngx_int_t
  58. ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
  59. {
  60.     if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
  61.         return NGX_ERROR;
  62.     }

  63.     us->peer.init = ngx_http_upstream_init_ip_hash_peer;

  64.     return NGX_OK;
  65. }


  66. static ngx_int_t
  67. ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
  68.     ngx_http_upstream_srv_conf_t *us)
  69. {
  70.     struct sockaddr_in                     *sin;
  71. #if (NGX_HAVE_INET6)
  72.     struct sockaddr_in6                    *sin6;
  73. #endif
  74.     ngx_http_upstream_ip_hash_peer_data_t  *iphp;

  75.     iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
  76.     if (iphp == NULL) {
  77.         return NGX_ERROR;
  78.     }

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

  80.     if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
  81.         return NGX_ERROR;
  82.     }

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

  84.     switch (r->connection->sockaddr->sa_family) {

  85.     case AF_INET:
  86.         sin = (struct sockaddr_in *) r->connection->sockaddr;
  87.         iphp->addr = (u_char *) &sin->sin_addr.s_addr;
  88.         iphp->addrlen = 3;
  89.         break;

  90. #if (NGX_HAVE_INET6)
  91.     case AF_INET6:
  92.         sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
  93.         iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;
  94.         iphp->addrlen = 16;
  95.         break;
  96. #endif

  97.     default:
  98.         iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;
  99.         iphp->addrlen = 3;
  100.     }

  101.     iphp->hash = 89;
  102.     iphp->tries = 0;
  103.     iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

  104.     return NGX_OK;
  105. }


  106. static ngx_int_t
  107. ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
  108. {
  109.     ngx_http_upstream_ip_hash_peer_data_t  *iphp = data;

  110.     time_t                        now;
  111.     ngx_int_t                     w;
  112.     uintptr_t                     m;
  113.     ngx_uint_t                    i, n, p, hash;
  114.     ngx_http_upstream_rr_peer_t  *peer;

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

  117.     /* TODO: cached */

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

  121.     now = ngx_time();

  122.     pc->cached = 0;
  123.     pc->connection = NULL;

  124.     hash = iphp->hash;

  125.     for ( ;; ) {

  126.         for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {
  127.             hash = (hash * 113 + iphp->addr[i]) % 6271;
  128.         }

  129.         if (!iphp->rrp.peers->weighted) {
  130.             p = hash % iphp->rrp.peers->number;

  131.         } else {
  132.             w = hash % iphp->rrp.peers->total_weight;

  133.             for (i = 0; i < iphp->rrp.peers->number; i++) {
  134.                 w -= iphp->rrp.peers->peer[i].weight;
  135.                 if (w < 0) {
  136.                     break;
  137.                 }
  138.             }

  139.             p = i;
  140.         }

  141.         n = p / (8 * sizeof(uintptr_t));
  142.         m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));

  143.         if (iphp->rrp.tried[n] & m) {
  144.             goto next;
  145.         }

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

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

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

  150.         if (peer->down) {
  151.             goto next_try;
  152.         }

  153.         if (peer->max_fails
  154.             && peer->fails >= peer->max_fails
  155.             && now - peer->checked <= peer->fail_timeout)
  156.         {
  157.             goto next_try;
  158.         }

  159.         break;

  160.     next_try:

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

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

  163.         pc->tries--;

  164.     next:

  165.         if (++iphp->tries > 20) {
  166.             return iphp->get_rr_peer(pc, &iphp->rrp);
  167.         }
  168.     }

  169.     iphp->rrp.current = p;

  170.     pc->sockaddr = peer->sockaddr;
  171.     pc->socklen = peer->socklen;
  172.     pc->name = &peer->name;

  173.     if (now - peer->checked > peer->fail_timeout) {
  174.         peer->checked = now;
  175.     }

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

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

  179.     return NGX_OK;
  180. }


  181. static char *
  182. ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  183. {
  184.     ngx_http_upstream_srv_conf_t  *uscf;

  185.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  186.     if (uscf->peer.init_upstream) {
  187.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  188.                            "load balancing method redefined");
  189.     }

  190.     uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;

  191.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  192.                   |NGX_HTTP_UPSTREAM_WEIGHT
  193.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  194.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  195.                   |NGX_HTTP_UPSTREAM_DOWN;

  196.     return NGX_CONF_OK;
  197. }