src/http/modules/ngx_http_upstream_hash_module.c - nginx source code

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.     for ( ;; ) {

  153.         /*
  154.          * Hash expression is compatible with Cache::Memcached:
  155.          * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH
  156.          * with REHASH omitted at the first iteration.
  157.          */

  158.         ngx_crc32_init(hash);

  159.         if (hp->rehash > 0) {
  160.             size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;
  161.             ngx_crc32_update(&hash, buf, size);
  162.         }

  163.         ngx_crc32_update(&hash, hp->key.data, hp->key.len);
  164.         ngx_crc32_final(hash);

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

  166.         hp->hash += hash;
  167.         hp->rehash++;

  168.         w = hp->hash % hp->rrp.peers->total_weight;
  169.         peer = hp->rrp.peers->peer;
  170.         p = 0;

  171.         while (w >= peer->weight) {
  172.             w -= peer->weight;
  173.             peer = peer->next;
  174.             p++;
  175.         }

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

  178.         if (hp->rrp.tried[n] & m) {
  179.             goto next;
  180.         }

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

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

  184.         if (peer->down) {
  185.             ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  186.             goto next;
  187.         }

  188.         if (peer->max_fails
  189.             && peer->fails >= peer->max_fails
  190.             && now - peer->checked <= peer->fail_timeout)
  191.         {
  192.             ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  193.             goto next;
  194.         }

  195.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  196.             ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  197.             goto next;
  198.         }

  199.         break;

  200.     next:

  201.         if (++hp->tries > 20) {
  202.             ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  203.             return hp->get_rr_peer(pc, &hp->rrp);
  204.         }
  205.     }

  206.     hp->rrp.current = peer;
  207.     ngx_http_upstream_rr_peer_ref(hp->rrp.peers, peer);

  208.     pc->sockaddr = peer->sockaddr;
  209.     pc->socklen = peer->socklen;
  210.     pc->name = &peer->name;

  211.     peer->conns++;

  212.     if (now - peer->checked > peer->fail_timeout) {
  213.         peer->checked = now;
  214.     }

  215.     ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  216.     ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);

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

  218.     return NGX_OK;
  219. }


  220. static ngx_int_t
  221. ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
  222. {
  223.     if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
  224.         return NGX_ERROR;
  225.     }

  226.     us->peer.init = ngx_http_upstream_init_chash_peer;

  227. #if (NGX_HTTP_UPSTREAM_ZONE)
  228.     if (us->shm_zone) {
  229.         return NGX_OK;
  230.     }
  231. #endif

  232.     return ngx_http_upstream_update_chash(cf->pool, us);
  233. }


  234. static ngx_int_t
  235. ngx_http_upstream_update_chash(ngx_pool_t *pool,
  236.     ngx_http_upstream_srv_conf_t *us)
  237. {
  238.     u_char                             *host, *port, c;
  239.     size_t                              host_len, port_len, size;
  240.     uint32_t                            hash, base_hash;
  241.     ngx_str_t                          *server;
  242.     ngx_uint_t                          npoints, i, j;
  243.     ngx_http_upstream_rr_peer_t        *peer;
  244.     ngx_http_upstream_rr_peers_t       *peers;
  245.     ngx_http_upstream_chash_points_t   *points;
  246.     ngx_http_upstream_hash_srv_conf_t  *hcf;
  247.     union {
  248.         uint32_t                        value;
  249.         u_char                          byte[4];
  250.     } prev_hash;

  251.     hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);

  252.     if (hcf->points) {
  253.         ngx_free(hcf->points);
  254.         hcf->points = NULL;
  255.     }

  256.     peers = us->peer.data;
  257.     npoints = peers->total_weight * 160;

  258.     size = sizeof(ngx_http_upstream_chash_points_t)
  259.            - sizeof(ngx_http_upstream_chash_point_t)
  260.            + sizeof(ngx_http_upstream_chash_point_t) * npoints;

  261.     points = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);
  262.     if (points == NULL) {
  263.         return NGX_ERROR;
  264.     }

  265.     points->number = 0;

  266.     if (npoints == 0) {
  267.         hcf->points = points;
  268.         return NGX_OK;
  269.     }

  270.     for (peer = peers->peer; peer; peer = peer->next) {
  271.         server = &peer->server;

  272.         /*
  273.          * Hash expression is compatible with Cache::Memcached::Fast:
  274.          * crc32(HOST \0 PORT PREV_HASH).
  275.          */

  276.         if (server->len >= 5
  277.             && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)
  278.         {
  279.             host = server->data + 5;
  280.             host_len = server->len - 5;
  281.             port = NULL;
  282.             port_len = 0;
  283.             goto done;
  284.         }

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

  287.             if (c == ':') {
  288.                 host = server->data;
  289.                 host_len = server->len - j - 1;
  290.                 port = server->data + server->len - j;
  291.                 port_len = j;
  292.                 goto done;
  293.             }

  294.             if (c < '0' || c > '9') {
  295.                 break;
  296.             }
  297.         }

  298.         host = server->data;
  299.         host_len = server->len;
  300.         port = NULL;
  301.         port_len = 0;

  302.     done:

  303.         ngx_crc32_init(base_hash);
  304.         ngx_crc32_update(&base_hash, host, host_len);
  305.         ngx_crc32_update(&base_hash, (u_char *) "", 1);
  306.         ngx_crc32_update(&base_hash, port, port_len);

  307.         prev_hash.value = 0;
  308.         npoints = peer->weight * 160;

  309.         for (j = 0; j < npoints; j++) {
  310.             hash = base_hash;

  311.             ngx_crc32_update(&hash, prev_hash.byte, 4);
  312.             ngx_crc32_final(hash);

  313.             points->point[points->number].hash = hash;
  314.             points->point[points->number].server = server;
  315.             points->number++;

  316. #if (NGX_HAVE_LITTLE_ENDIAN)
  317.             prev_hash.value = hash;
  318. #else
  319.             prev_hash.byte[0] = (u_char) (hash & 0xff);
  320.             prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff);
  321.             prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff);
  322.             prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff);
  323. #endif
  324.         }
  325.     }

  326.     ngx_qsort(points->point,
  327.               points->number,
  328.               sizeof(ngx_http_upstream_chash_point_t),
  329.               ngx_http_upstream_chash_cmp_points);

  330.     for (i = 0, j = 1; j < points->number; j++) {
  331.         if (points->point[i].hash != points->point[j].hash) {
  332.             points->point[++i] = points->point[j];
  333.         }
  334.     }

  335.     points->number = i + 1;

  336.     hcf->points = points;

  337.     return NGX_OK;
  338. }


  339. static int ngx_libc_cdecl
  340. ngx_http_upstream_chash_cmp_points(const void *one, const void *two)
  341. {
  342.     ngx_http_upstream_chash_point_t *first =
  343.                                        (ngx_http_upstream_chash_point_t *) one;
  344.     ngx_http_upstream_chash_point_t *second =
  345.                                        (ngx_http_upstream_chash_point_t *) two;

  346.     if (first->hash < second->hash) {
  347.         return -1;

  348.     } else if (first->hash > second->hash) {
  349.         return 1;

  350.     } else {
  351.         return 0;
  352.     }
  353. }


  354. static ngx_uint_t
  355. ngx_http_upstream_find_chash_point(ngx_http_upstream_chash_points_t *points,
  356.     uint32_t hash)
  357. {
  358.     ngx_uint_t                        i, j, k;
  359.     ngx_http_upstream_chash_point_t  *point;

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

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

  362.     i = 0;
  363.     j = points->number;

  364.     while (i < j) {
  365.         k = (i + j) / 2;

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

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

  370.         } else {
  371.             return k;
  372.         }
  373.     }

  374.     return i;
  375. }


  376. static ngx_int_t
  377. ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
  378.     ngx_http_upstream_srv_conf_t *us)
  379. {
  380.     uint32_t                             hash;
  381.     ngx_http_upstream_hash_srv_conf_t   *hcf;
  382.     ngx_http_upstream_hash_peer_data_t  *hp;

  383.     if (ngx_http_upstream_init_hash_peer(r, us) != NGX_OK) {
  384.         return NGX_ERROR;
  385.     }

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

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

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

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

  391. #if (NGX_HTTP_UPSTREAM_ZONE)
  392.     if (hp->rrp.peers->config
  393.         && (hcf->points == NULL || hcf->config != *hp->rrp.peers->config))
  394.     {
  395.         if (ngx_http_upstream_update_chash(NULL, us) != NGX_OK) {
  396.             ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  397.             return NGX_ERROR;
  398.         }

  399.         hcf->config = *hp->rrp.peers->config;
  400.     }
  401. #endif

  402.     if (hcf->points->number) {
  403.         hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
  404.     }

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

  406.     return NGX_OK;
  407. }


  408. static ngx_int_t
  409. ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
  410. {
  411.     ngx_http_upstream_hash_peer_data_t  *hp = data;

  412.     time_t                              now;
  413.     intptr_t                            m;
  414.     ngx_str_t                          *server;
  415.     ngx_int_t                           total;
  416.     ngx_uint_t                          i, n, best_i;
  417.     ngx_http_upstream_rr_peer_t        *peer, *best;
  418.     ngx_http_upstream_chash_point_t    *point;
  419.     ngx_http_upstream_chash_points_t   *points;
  420.     ngx_http_upstream_hash_srv_conf_t  *hcf;

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

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

  424.     if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {
  425.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  426.         return hp->get_rr_peer(pc, &hp->rrp);
  427.     }

  428.     pc->cached = 0;
  429.     pc->connection = NULL;

  430.     if (hp->rrp.peers->number == 0) {
  431.         pc->name = hp->rrp.peers->name;
  432.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  433.         return NGX_BUSY;
  434.     }

  435. #if (NGX_HTTP_UPSTREAM_ZONE)
  436.     if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) {
  437.         pc->name = hp->rrp.peers->name;
  438.         ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  439.         return NGX_BUSY;
  440.     }
  441. #endif

  442.     now = ngx_time();
  443.     hcf = hp->conf;

  444.     points = hcf->points;
  445.     point = &points->point[0];

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

  448.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  449.                        "consistent hash peer:%uD, server:\"%V\"",
  450.                        hp->hash, server);

  451.         best = NULL;
  452.         best_i = 0;
  453.         total = 0;

  454.         for (peer = hp->rrp.peers->peer, i = 0;
  455.              peer;
  456.              peer = peer->next, i++)
  457.         {
  458.             n = i / (8 * sizeof(uintptr_t));
  459.             m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

  460.             if (hp->rrp.tried[n] & m) {
  461.                 continue;
  462.             }

  463.             if (peer->down) {
  464.                 continue;
  465.             }

  466.             if (peer->max_fails
  467.                 && peer->fails >= peer->max_fails
  468.                 && now - peer->checked <= peer->fail_timeout)
  469.             {
  470.                 continue;
  471.             }

  472.             if (peer->max_conns && peer->conns >= peer->max_conns) {
  473.                 continue;
  474.             }

  475.             if (peer->server.len != server->len
  476.                 || ngx_strncmp(peer->server.data, server->data, server->len)
  477.                    != 0)
  478.             {
  479.                 continue;
  480.             }

  481.             peer->current_weight += peer->effective_weight;
  482.             total += peer->effective_weight;

  483.             if (peer->effective_weight < peer->weight) {
  484.                 peer->effective_weight++;
  485.             }

  486.             if (best == NULL || peer->current_weight > best->current_weight) {
  487.                 best = peer;
  488.                 best_i = i;
  489.             }
  490.         }

  491.         if (best) {
  492.             best->current_weight -= total;
  493.             goto found;
  494.         }

  495.         hp->hash++;
  496.         hp->tries++;

  497.         if (hp->tries > 20) {
  498.             ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
  499.             return hp->get_rr_peer(pc, &hp->rrp);
  500.         }
  501.     }

  502. found:

  503.     hp->rrp.current = best;
  504.     ngx_http_upstream_rr_peer_ref(hp->rrp.peers, best);

  505.     pc->sockaddr = best->sockaddr;
  506.     pc->socklen = best->socklen;
  507.     pc->name = &best->name;

  508.     best->conns++;

  509.     if (now - best->checked > best->fail_timeout) {
  510.         best->checked = now;
  511.     }

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

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

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

  516.     return NGX_OK;
  517. }


  518. static void *
  519. ngx_http_upstream_hash_create_conf(ngx_conf_t *cf)
  520. {
  521.     ngx_http_upstream_hash_srv_conf_t  *conf;

  522.     conf = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_hash_srv_conf_t));
  523.     if (conf == NULL) {
  524.         return NULL;
  525.     }

  526.     conf->points = NULL;

  527.     return conf;
  528. }


  529. static char *
  530. ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  531. {
  532.     ngx_http_upstream_hash_srv_conf_t  *hcf = conf;

  533.     ngx_str_t                         *value;
  534.     ngx_http_upstream_srv_conf_t      *uscf;
  535.     ngx_http_compile_complex_value_t   ccv;

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

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

  538.     ccv.cf = cf;
  539.     ccv.value = &value[1];
  540.     ccv.complex_value = &hcf->key;

  541.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  542.         return NGX_CONF_ERROR;
  543.     }

  544.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  545.     if (uscf->peer.init_upstream) {
  546.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  547.                            "load balancing method redefined");
  548.     }

  549.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  550.                   |NGX_HTTP_UPSTREAM_MODIFY
  551.                   |NGX_HTTP_UPSTREAM_WEIGHT
  552.                   |NGX_HTTP_UPSTREAM_MAX_CONNS
  553.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  554.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  555.                   |NGX_HTTP_UPSTREAM_DOWN;

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

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

  560.     } else {
  561.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  562.                            "invalid parameter \"%V\"", &value[2]);
  563.         return NGX_CONF_ERROR;
  564.     }

  565.     return NGX_CONF_OK;
  566. }