src/http/modules/ngx_http_upstream_hash_module.c - nginx

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Roman Arutyunyan
  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.     uint32_t                            hash;
  10.     ngx_str_t                          *server;
  11. } ngx_http_upstream_chash_point_t;


  12. typedef struct {
  13.     ngx_uint_t                          number;
  14.     ngx_http_upstream_chash_point_t     point[1];
  15. } ngx_http_upstream_chash_points_t;


  16. typedef struct {
  17.     ngx_http_complex_value_t            key;
  18. #if (NGX_HTTP_UPSTREAM_ZONE)
  19.     ngx_uint_t                          config;
  20. #endif
  21.     ngx_http_upstream_chash_points_t   *points;
  22. } ngx_http_upstream_hash_srv_conf_t;


  23. typedef struct {
  24.     /* the round robin data must be first */
  25.     ngx_http_upstream_rr_peer_data_t    rrp;
  26.     ngx_http_upstream_hash_srv_conf_t  *conf;
  27.     ngx_str_t                           key;
  28.     ngx_uint_t                          tries;
  29.     ngx_uint_t                          rehash;
  30.     uint32_t                            hash;
  31.     ngx_event_get_peer_pt               get_rr_peer;
  32. } ngx_http_upstream_hash_peer_data_t;


  33. static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,
  34.     ngx_http_upstream_srv_conf_t *us);
  35. static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
  36.     ngx_http_upstream_srv_conf_t *us);
  37. static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,
  38.     void *data);

  39. static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,
  40.     ngx_http_upstream_srv_conf_t *us);
  41. static ngx_int_t ngx_http_upstream_update_chash(ngx_pool_t *pool,
  42.     ngx_http_upstream_srv_conf_t *us);
  43. static int ngx_libc_cdecl
  44.     ngx_http_upstream_chash_cmp_points(const void *one, const void *two);
  45. static ngx_uint_t ngx_http_upstream_find_chash_point(
  46.     ngx_http_upstream_chash_points_t *points, uint32_t hash);
  47. static ngx_int_t ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
  48.     ngx_http_upstream_srv_conf_t *us);
  49. static ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc,
  50.     void *data);

  51. static void *ngx_http_upstream_hash_create_conf(ngx_conf_t *cf);
  52. static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,
  53.     void *conf);


  54. static ngx_command_t  ngx_http_upstream_hash_commands[] = {

  55.     { ngx_string("hash"),
  56.       NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
  57.       ngx_http_upstream_hash,
  58.       NGX_HTTP_SRV_CONF_OFFSET,
  59.       0,
  60.       NULL },

  61.       ngx_null_command
  62. };


  63. static ngx_http_module_t  ngx_http_upstream_hash_module_ctx = {
  64.     NULL,                                  /* preconfiguration */
  65.     NULL,                                  /* postconfiguration */

  66.     NULL,                                  /* create main configuration */
  67.     NULL,                                  /* init main configuration */

  68.     ngx_http_upstream_hash_create_conf,    /* create server configuration */
  69.     NULL,                                  /* merge server configuration */

  70.     NULL,                                  /* create location configuration */
  71.     NULL                                   /* merge location configuration */
  72. };


  73. ngx_module_t  ngx_http_upstream_hash_module = {
  74.     NGX_MODULE_V1,
  75.     &ngx_http_upstream_hash_module_ctx,    /* module context */
  76.     ngx_http_upstream_hash_commands,       /* module directives */
  77.     NGX_HTTP_MODULE,                       /* module type */
  78.     NULL,                                  /* init master */
  79.     NULL,                                  /* init module */
  80.     NULL,                                  /* init process */
  81.     NULL,                                  /* init thread */
  82.     NULL,                                  /* exit thread */
  83.     NULL,                                  /* exit process */
  84.     NULL,                                  /* exit master */
  85.     NGX_MODULE_V1_PADDING
  86. };


  87. static ngx_int_t
  88. ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
  89. {
  90.     if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
  91.         return NGX_ERROR;
  92.     }

  93.     us->peer.init = ngx_http_upstream_init_hash_peer;

  94.     return NGX_OK;
  95. }


  96. static ngx_int_t
  97. ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
  98.     ngx_http_upstream_srv_conf_t *us)
  99. {
  100.     ngx_http_upstream_hash_srv_conf_t   *hcf;
  101.     ngx_http_upstream_hash_peer_data_t  *hp;

  102.     hp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t));
  103.     if (hp == NULL) {
  104.         return NGX_ERROR;
  105.     }

  106.     r->upstream->peer.data = &hp->rrp;

  107.     if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
  108.         return NGX_ERROR;
  109.     }

  110.     r->upstream->peer.get = ngx_http_upstream_get_hash_peer;

  111.     hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

  112.     if (ngx_http_complex_value(r, &hcf->key, &hp->key) != NGX_OK) {
  113.         return NGX_ERROR;
  114.     }

  115.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  116.                    "upstream hash key:\"%V\"", &hp->key);

  117.     hp->conf = hcf;
  118.     hp->tries = 0;
  119.     hp->rehash = 0;
  120.     hp->hash = 0;
  121.     hp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;

  122.     return NGX_OK;
  123. }


  124. static ngx_int_t
  125. ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
  126. {
  127.     ngx_http_upstream_hash_peer_data_t  *hp = data;

  128.     time_t                        now;
  129.     u_char                        buf[NGX_INT_T_LEN];
  130.     size_t                        size;
  131.     uint32_t                      hash;
  132.     ngx_int_t                     w;
  133.     uintptr_t                     m;
  134.     ngx_uint_t                    n, p;
  135.     ngx_http_upstream_rr_peer_t  *peer;

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

  138.     ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);

  139.     if (hp->tries > 20 || hp->rrp.peers->number < 2 || hp->key.len == 0) {
  140.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  141.         return hp->get_rr_peer(pc, &hp->rrp);
  142.     }

  143. #if (NGX_HTTP_UPSTREAM_ZONE)
  144.     if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) {
  145.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  146.         return hp->get_rr_peer(pc, &hp->rrp);
  147.     }
  148. #endif

  149.     now = ngx_time();

  150.     pc->cached = 0;
  151.     pc->connection = NULL;

  152. #if (NGX_HTTP_UPSTREAM_SID)
  153.     peer = ngx_http_upstream_get_rr_peer_by_sid(&hp->rrp, pc->hint, &p, 1);

  154.     if (peer) {
  155.         n = p / (8 * sizeof(uintptr_t));
  156.         m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));

  157.         goto found;
  158.     }
  159. #endif

  160.     for ( ;; ) {

  161.         /*
  162.          * Hash expression is compatible with Cache::Memcached:
  163.          * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH
  164.          * with REHASH omitted at the first iteration.
  165.          */

  166.         ngx_crc32_init(hash);

  167.         if (hp->rehash > 0) {
  168.             size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;
  169.             ngx_crc32_update(&hash, buf, size);
  170.         }

  171.         ngx_crc32_update(&hash, hp->key.data, hp->key.len);
  172.         ngx_crc32_final(hash);

  173.         hash = (hash >> 16) & 0x7fff;

  174.         hp->hash += hash;
  175.         hp->rehash++;

  176.         w = hp->hash % hp->rrp.peers->total_weight;
  177.         peer = hp->rrp.peers->peer;
  178.         p = 0;

  179.         while (w >= peer->weight) {
  180.             w -= peer->weight;
  181.             peer = peer->next;
  182.             p++;
  183.         }

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

  186.         if (hp->rrp.tried[n] & m) {
  187.             goto next;
  188.         }

  189.         ngx_http_upstream_rr_peer_lock(hp->rrp.peers, peer);

  190.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  191.                        "get hash peer, value:%uD, peer:%ui", hp->hash, p);

  192.         if (peer->down) {
  193.             ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  194.             goto next;
  195.         }

  196.         if (peer->max_fails
  197.             && peer->fails >= peer->max_fails
  198.             && now - peer->checked <= peer->fail_timeout)
  199.         {
  200.             ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  201.             goto next;
  202.         }

  203.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  204.             ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  205.             goto next;
  206.         }

  207.         break;

  208.     next:

  209.         if (++hp->tries > 20) {
  210.             ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  211.             return hp->get_rr_peer(pc, &hp->rrp);
  212.         }
  213.     }

  214. #if (NGX_HTTP_UPSTREAM_SID)
  215. found:
  216. #endif

  217.     hp->rrp.current = peer;
  218.     ngx_http_upstream_rr_peer_ref(hp->rrp.peers, peer);

  219.     pc->sockaddr = peer->sockaddr;
  220.     pc->socklen = peer->socklen;
  221.     pc->name = &peer->name;

  222. #if (NGX_HTTP_UPSTREAM_SID)
  223.     pc->sid = &peer->sid;
  224. #endif

  225.     peer->conns++;

  226.     if (now - peer->checked > peer->fail_timeout) {
  227.         peer->checked = now;
  228.     }

  229.     ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  230.     ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);

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

  232.     return NGX_OK;
  233. }


  234. static ngx_int_t
  235. ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
  236. {
  237.     if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
  238.         return NGX_ERROR;
  239.     }

  240.     us->peer.init = ngx_http_upstream_init_chash_peer;

  241. #if (NGX_HTTP_UPSTREAM_ZONE)
  242.     if (us->shm_zone) {
  243.         return NGX_OK;
  244.     }
  245. #endif

  246.     return ngx_http_upstream_update_chash(cf->pool, us);
  247. }


  248. static ngx_int_t
  249. ngx_http_upstream_update_chash(ngx_pool_t *pool,
  250.     ngx_http_upstream_srv_conf_t *us)
  251. {
  252.     u_char                             *host, *port, c;
  253.     size_t                              host_len, port_len, size;
  254.     uint32_t                            hash, base_hash;
  255.     ngx_str_t                          *server;
  256.     ngx_uint_t                          npoints, i, j;
  257.     ngx_http_upstream_rr_peer_t        *peer;
  258.     ngx_http_upstream_rr_peers_t       *peers;
  259.     ngx_http_upstream_chash_points_t   *points;
  260.     ngx_http_upstream_hash_srv_conf_t  *hcf;
  261.     union {
  262.         uint32_t                        value;
  263.         u_char                          byte[4];
  264.     } prev_hash;

  265.     hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

  266.     if (hcf->points) {
  267.         ngx_free(hcf->points);
  268.         hcf->points = NULL;
  269.     }

  270.     peers = us->peer.data;
  271.     npoints = peers->total_weight * 160;

  272.     size = sizeof(ngx_http_upstream_chash_points_t)
  273.            - sizeof(ngx_http_upstream_chash_point_t)
  274.            + sizeof(ngx_http_upstream_chash_point_t) * npoints;

  275.     points = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);
  276.     if (points == NULL) {
  277.         return NGX_ERROR;
  278.     }

  279.     points->number = 0;

  280.     if (npoints == 0) {
  281.         hcf->points = points;
  282.         return NGX_OK;
  283.     }

  284.     for (peer = peers->peer; peer; peer = peer->next) {
  285.         server = &peer->server;

  286.         /*
  287.          * Hash expression is compatible with Cache::Memcached::Fast:
  288.          * crc32(HOST \0 PORT PREV_HASH).
  289.          */

  290.         if (server->len >= 5
  291.             && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)
  292.         {
  293.             host = server->data + 5;
  294.             host_len = server->len - 5;
  295.             port = NULL;
  296.             port_len = 0;
  297.             goto done;
  298.         }

  299.         for (j = 0; j < server->len; j++) {
  300.             c = server->data[server->len - j - 1];

  301.             if (c == ':') {
  302.                 host = server->data;
  303.                 host_len = server->len - j - 1;
  304.                 port = server->data + server->len - j;
  305.                 port_len = j;
  306.                 goto done;
  307.             }

  308.             if (c < '0' || c > '9') {
  309.                 break;
  310.             }
  311.         }

  312.         host = server->data;
  313.         host_len = server->len;
  314.         port = NULL;
  315.         port_len = 0;

  316.     done:

  317.         ngx_crc32_init(base_hash);
  318.         ngx_crc32_update(&base_hash, host, host_len);
  319.         ngx_crc32_update(&base_hash, (u_char *) "", 1);
  320.         ngx_crc32_update(&base_hash, port, port_len);

  321.         prev_hash.value = 0;
  322.         npoints = peer->weight * 160;

  323.         for (j = 0; j < npoints; j++) {
  324.             hash = base_hash;

  325.             ngx_crc32_update(&hash, prev_hash.byte, 4);
  326.             ngx_crc32_final(hash);

  327.             points->point[points->number].hash = hash;
  328.             points->point[points->number].server = server;
  329.             points->number++;

  330. #if (NGX_HAVE_LITTLE_ENDIAN)
  331.             prev_hash.value = hash;
  332. #else
  333.             prev_hash.byte[0] = (u_char) (hash & 0xff);
  334.             prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff);
  335.             prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff);
  336.             prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff);
  337. #endif
  338.         }
  339.     }

  340.     ngx_qsort(points->point,
  341.               points->number,
  342.               sizeof(ngx_http_upstream_chash_point_t),
  343.               ngx_http_upstream_chash_cmp_points);

  344.     for (i = 0, j = 1; j < points->number; j++) {
  345.         if (points->point[i].hash != points->point[j].hash) {
  346.             points->point[++i] = points->point[j];
  347.         }
  348.     }

  349.     points->number = i + 1;

  350.     hcf->points = points;

  351.     return NGX_OK;
  352. }


  353. static int ngx_libc_cdecl
  354. ngx_http_upstream_chash_cmp_points(const void *one, const void *two)
  355. {
  356.     ngx_http_upstream_chash_point_t *first =
  357.                                        (ngx_http_upstream_chash_point_t *) one;
  358.     ngx_http_upstream_chash_point_t *second =
  359.                                        (ngx_http_upstream_chash_point_t *) two;

  360.     if (first->hash < second->hash) {
  361.         return -1;

  362.     } else if (first->hash > second->hash) {
  363.         return 1;

  364.     } else {
  365.         return 0;
  366.     }
  367. }


  368. static ngx_uint_t
  369. ngx_http_upstream_find_chash_point(ngx_http_upstream_chash_points_t *points,
  370.     uint32_t hash)
  371. {
  372.     ngx_uint_t                        i, j, k;
  373.     ngx_http_upstream_chash_point_t  *point;

  374.     /* find first point >= hash */

  375.     point = &points->point[0];

  376.     i = 0;
  377.     j = points->number;

  378.     while (i < j) {
  379.         k = (i + j) / 2;

  380.         if (hash > point[k].hash) {
  381.             i = k + 1;

  382.         } else if (hash < point[k].hash) {
  383.             j = k;

  384.         } else {
  385.             return k;
  386.         }
  387.     }

  388.     return i;
  389. }


  390. static ngx_int_t
  391. ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
  392.     ngx_http_upstream_srv_conf_t *us)
  393. {
  394.     uint32_t                             hash;
  395.     ngx_http_upstream_hash_srv_conf_t   *hcf;
  396.     ngx_http_upstream_hash_peer_data_t  *hp;

  397.     if (ngx_http_upstream_init_hash_peer(r, us) != NGX_OK) {
  398.         return NGX_ERROR;
  399.     }

  400.     r->upstream->peer.get = ngx_http_upstream_get_chash_peer;

  401.     hp = r->upstream->peer.data;
  402.     hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

  403.     hash = ngx_crc32_long(hp->key.data, hp->key.len);

  404.     ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);

  405. #if (NGX_HTTP_UPSTREAM_ZONE)
  406.     if (hp->rrp.peers->config
  407.         && (hcf->points == NULL || hcf->config != *hp->rrp.peers->config))
  408.     {
  409.         if (ngx_http_upstream_update_chash(NULL, us) != NGX_OK) {
  410.             ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  411.             return NGX_ERROR;
  412.         }

  413.         hcf->config = *hp->rrp.peers->config;
  414.     }
  415. #endif

  416.     if (hcf->points->number) {
  417.         hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
  418.     }

  419.     ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);

  420.     return NGX_OK;
  421. }


  422. static ngx_int_t
  423. ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
  424. {
  425.     ngx_http_upstream_hash_peer_data_t  *hp = data;

  426.     time_t                              now;
  427.     intptr_t                            m;
  428.     ngx_str_t                          *server;
  429.     ngx_int_t                           total;
  430.     ngx_uint_t                          i, n, best_i;
  431.     ngx_http_upstream_rr_peer_t        *peer, *best;
  432.     ngx_http_upstream_chash_point_t    *point;
  433.     ngx_http_upstream_chash_points_t   *points;
  434.     ngx_http_upstream_hash_srv_conf_t  *hcf;

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

  437.     ngx_http_upstream_rr_peers_wlock(hp->rrp.peers);

  438.     if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {
  439.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  440.         return hp->get_rr_peer(pc, &hp->rrp);
  441.     }

  442.     pc->cached = 0;
  443.     pc->connection = NULL;

  444.     if (hp->rrp.peers->number == 0) {
  445.         pc->name = hp->rrp.peers->name;
  446.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  447.         return NGX_BUSY;
  448.     }

  449. #if (NGX_HTTP_UPSTREAM_ZONE)
  450.     if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) {
  451.         pc->name = hp->rrp.peers->name;
  452.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  453.         return NGX_BUSY;
  454.     }
  455. #endif

  456.     now = ngx_time();
  457.     hcf = hp->conf;

  458.     points = hcf->points;
  459.     point = &points->point[0];

  460. #if (NGX_HTTP_UPSTREAM_SID)
  461.     best = ngx_http_upstream_get_rr_peer_by_sid(&hp->rrp, pc->hint, &best_i, 0);

  462.     if (best) {
  463.         goto found;
  464.     }
  465. #endif

  466.     for ( ;; ) {
  467.         server = point[hp->hash % points->number].server;

  468.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  469.                        "consistent hash peer:%uD, server:\"%V\"",
  470.                        hp->hash, server);

  471.         best = NULL;
  472.         best_i = 0;
  473.         total = 0;

  474.         for (peer = hp->rrp.peers->peer, i = 0;
  475.              peer;
  476.              peer = peer->next, i++)
  477.         {
  478.             n = i / (8 * sizeof(uintptr_t));
  479.             m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

  480.             if (hp->rrp.tried[n] & m) {
  481.                 continue;
  482.             }

  483.             if (peer->down) {
  484.                 continue;
  485.             }

  486.             if (peer->max_fails
  487.                 && peer->fails >= peer->max_fails
  488.                 && now - peer->checked <= peer->fail_timeout)
  489.             {
  490.                 continue;
  491.             }

  492.             if (peer->max_conns && peer->conns >= peer->max_conns) {
  493.                 continue;
  494.             }

  495.             if (peer->server.len != server->len
  496.                 || ngx_strncmp(peer->server.data, server->data, server->len)
  497.                    != 0)
  498.             {
  499.                 continue;
  500.             }

  501.             peer->current_weight += peer->effective_weight;
  502.             total += peer->effective_weight;

  503.             if (peer->effective_weight < peer->weight) {
  504.                 peer->effective_weight++;
  505.             }

  506.             if (best == NULL || peer->current_weight > best->current_weight) {
  507.                 best = peer;
  508.                 best_i = i;
  509.             }
  510.         }

  511.         if (best) {
  512.             best->current_weight -= total;
  513.             goto found;
  514.         }

  515.         hp->hash++;
  516.         hp->tries++;

  517.         if (hp->tries > 20) {
  518.             ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  519.             return hp->get_rr_peer(pc, &hp->rrp);
  520.         }
  521.     }

  522. found:

  523.     hp->rrp.current = best;
  524.     ngx_http_upstream_rr_peer_ref(hp->rrp.peers, best);

  525.     pc->sockaddr = best->sockaddr;
  526.     pc->socklen = best->socklen;
  527.     pc->name = &best->name;

  528. #if (NGX_HTTP_UPSTREAM_SID)
  529.     pc->sid = &best->sid;
  530. #endif

  531.     best->conns++;

  532.     if (now - best->checked > best->fail_timeout) {
  533.         best->checked = now;
  534.     }

  535.     ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);

  536.     n = best_i / (8 * sizeof(uintptr_t));
  537.     m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));

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

  539.     return NGX_OK;
  540. }


  541. static void *
  542. ngx_http_upstream_hash_create_conf(ngx_conf_t *cf)
  543. {
  544.     ngx_http_upstream_hash_srv_conf_t  *conf;

  545.     conf = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_hash_srv_conf_t));
  546.     if (conf == NULL) {
  547.         return NULL;
  548.     }

  549.     conf->points = NULL;

  550.     return conf;
  551. }


  552. static char *
  553. ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  554. {
  555.     ngx_http_upstream_hash_srv_conf_t  *hcf = conf;

  556.     ngx_str_t                         *value;
  557.     ngx_http_upstream_srv_conf_t      *uscf;
  558.     ngx_http_compile_complex_value_t   ccv;

  559.     value = cf->args->elts;

  560.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  561.     ccv.cf = cf;
  562.     ccv.value = &value[1];
  563.     ccv.complex_value = &hcf->key;

  564.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  565.         return NGX_CONF_ERROR;
  566.     }

  567.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  568.     if (uscf->peer.init_upstream) {
  569.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  570.                            "load balancing method redefined");
  571.     }

  572.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  573.                   |NGX_HTTP_UPSTREAM_MODIFY
  574.                   |NGX_HTTP_UPSTREAM_WEIGHT
  575.                   |NGX_HTTP_UPSTREAM_MAX_CONNS
  576.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  577.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  578.                   |NGX_HTTP_UPSTREAM_DOWN;

  579.     if (cf->args->nelts == 2) {
  580.         uscf->peer.init_upstream = ngx_http_upstream_init_hash;

  581.     } else if (ngx_strcmp(value[2].data, "consistent") == 0) {
  582.         uscf->peer.init_upstream = ngx_http_upstream_init_chash;

  583.     } else {
  584.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  585.                            "invalid parameter \"%V\"", &value[2]);
  586.         return NGX_CONF_ERROR;
  587.     }

  588.     return NGX_CONF_OK;
  589. }