src/stream/ngx_stream_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_stream.h>


  8. typedef struct {
  9.     uint32_t                              hash;
  10.     ngx_str_t                            *server;
  11. } ngx_stream_upstream_chash_point_t;


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


  16. typedef struct {
  17. #if (NGX_STREAM_UPSTREAM_ZONE)
  18.     ngx_uint_t                            config;
  19. #endif
  20.     ngx_stream_complex_value_t            key;
  21.     ngx_stream_upstream_chash_points_t   *points;
  22. } ngx_stream_upstream_hash_srv_conf_t;


  23. typedef struct {
  24.     /* the round robin data must be first */
  25.     ngx_stream_upstream_rr_peer_data_t    rrp;
  26.     ngx_stream_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_stream_upstream_hash_peer_data_t;


  33. static ngx_int_t ngx_stream_upstream_init_hash(ngx_conf_t *cf,
  34.     ngx_stream_upstream_srv_conf_t *us);
  35. static ngx_int_t ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
  36.     ngx_stream_upstream_srv_conf_t *us);
  37. static ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc,
  38.     void *data);

  39. static ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf,
  40.     ngx_stream_upstream_srv_conf_t *us);
  41. static ngx_int_t ngx_stream_upstream_update_chash(ngx_pool_t *pool,
  42.     ngx_stream_upstream_srv_conf_t *us);
  43. static int ngx_libc_cdecl
  44.     ngx_stream_upstream_chash_cmp_points(const void *one, const void *two);
  45. static ngx_uint_t ngx_stream_upstream_find_chash_point(
  46.     ngx_stream_upstream_chash_points_t *points, uint32_t hash);
  47. static ngx_int_t ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,
  48.     ngx_stream_upstream_srv_conf_t *us);
  49. static ngx_int_t ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc,
  50.     void *data);

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


  54. static ngx_command_t  ngx_stream_upstream_hash_commands[] = {

  55.     { ngx_string("hash"),
  56.       NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,
  57.       ngx_stream_upstream_hash,
  58.       NGX_STREAM_SRV_CONF_OFFSET,
  59.       0,
  60.       NULL },

  61.       ngx_null_command
  62. };


  63. static ngx_stream_module_t  ngx_stream_upstream_hash_module_ctx = {
  64.     NULL,                                  /* preconfiguration */
  65.     NULL,                                  /* postconfiguration */

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

  68.     ngx_stream_upstream_hash_create_conf/* create server configuration */
  69.     NULL                                   /* merge server configuration */
  70. };


  71. ngx_module_t  ngx_stream_upstream_hash_module = {
  72.     NGX_MODULE_V1,
  73.     &ngx_stream_upstream_hash_module_ctx/* module context */
  74.     ngx_stream_upstream_hash_commands,     /* module directives */
  75.     NGX_STREAM_MODULE,                     /* module type */
  76.     NULL,                                  /* init master */
  77.     NULL,                                  /* init module */
  78.     NULL,                                  /* init process */
  79.     NULL,                                  /* init thread */
  80.     NULL,                                  /* exit thread */
  81.     NULL,                                  /* exit process */
  82.     NULL,                                  /* exit master */
  83.     NGX_MODULE_V1_PADDING
  84. };


  85. static ngx_int_t
  86. ngx_stream_upstream_init_hash(ngx_conf_t *cf,
  87.     ngx_stream_upstream_srv_conf_t *us)
  88. {
  89.     if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
  90.         return NGX_ERROR;
  91.     }

  92.     us->peer.init = ngx_stream_upstream_init_hash_peer;

  93.     return NGX_OK;
  94. }


  95. static ngx_int_t
  96. ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
  97.     ngx_stream_upstream_srv_conf_t *us)
  98. {
  99.     ngx_stream_upstream_hash_srv_conf_t   *hcf;
  100.     ngx_stream_upstream_hash_peer_data_t  *hp;

  101.     hp = ngx_palloc(s->connection->pool,
  102.                     sizeof(ngx_stream_upstream_hash_peer_data_t));
  103.     if (hp == NULL) {
  104.         return NGX_ERROR;
  105.     }

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

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

  110.     s->upstream->peer.get = ngx_stream_upstream_get_hash_peer;

  111.     hcf = ngx_stream_conf_upstream_srv_conf(us,
  112.                                             ngx_stream_upstream_hash_module);

  113.     if (ngx_stream_complex_value(s, &hcf->key, &hp->key) != NGX_OK) {
  114.         return NGX_ERROR;
  115.     }

  116.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  117.                    "upstream hash key:\"%V\"", &hp->key);

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

  123.     return NGX_OK;
  124. }


  125. static ngx_int_t
  126. ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
  127. {
  128.     ngx_stream_upstream_hash_peer_data_t *hp = data;

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

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

  139.     ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);

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

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

  150.     now = ngx_time();

  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_stream_upstream_rr_peer_lock(hp->rrp.peers, peer);

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

  184.         if (peer->down) {
  185.             ngx_stream_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_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  193.             goto next;
  194.         }

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

  199.         break;

  200.     next:

  201.         if (++hp->tries > 20) {
  202.             ngx_stream_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_stream_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_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);
  216.     ngx_stream_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_stream_upstream_init_chash(ngx_conf_t *cf,
  222.     ngx_stream_upstream_srv_conf_t *us)
  223. {
  224.     if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
  225.         return NGX_ERROR;
  226.     }

  227.     us->peer.init = ngx_stream_upstream_init_chash_peer;

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

  233.     return ngx_stream_upstream_update_chash(cf->pool, us);
  234. }


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

  252.     hcf = ngx_stream_conf_upstream_srv_conf(us,
  253.                                             ngx_stream_upstream_hash_module);

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

  258.     peers = us->peer.data;
  259.     npoints = peers->total_weight * 160;

  260.     size = sizeof(ngx_stream_upstream_chash_points_t)
  261.            - sizeof(ngx_stream_upstream_chash_point_t)
  262.            + sizeof(ngx_stream_upstream_chash_point_t) * npoints;

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

  267.     points->number = 0;

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

  272.     for (peer = peers->peer; peer; peer = peer->next) {
  273.         server = &peer->server;

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

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

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

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

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

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

  304.     done:

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

  309.         prev_hash.value = 0;
  310.         npoints = peer->weight * 160;

  311.         for (j = 0; j < npoints; j++) {
  312.             hash = base_hash;

  313.             ngx_crc32_update(&hash, prev_hash.byte, 4);
  314.             ngx_crc32_final(hash);

  315.             points->point[points->number].hash = hash;
  316.             points->point[points->number].server = server;
  317.             points->number++;

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

  328.     ngx_qsort(points->point,
  329.               points->number,
  330.               sizeof(ngx_stream_upstream_chash_point_t),
  331.               ngx_stream_upstream_chash_cmp_points);

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

  337.     points->number = i + 1;

  338.     hcf->points = points;

  339.     return NGX_OK;
  340. }


  341. static int ngx_libc_cdecl
  342. ngx_stream_upstream_chash_cmp_points(const void *one, const void *two)
  343. {
  344.     ngx_stream_upstream_chash_point_t *first =
  345.                                      (ngx_stream_upstream_chash_point_t *) one;
  346.     ngx_stream_upstream_chash_point_t *second =
  347.                                      (ngx_stream_upstream_chash_point_t *) two;

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

  350.     } else if (first->hash > second->hash) {
  351.         return 1;

  352.     } else {
  353.         return 0;
  354.     }
  355. }


  356. static ngx_uint_t
  357. ngx_stream_upstream_find_chash_point(ngx_stream_upstream_chash_points_t *points,
  358.     uint32_t hash)
  359. {
  360.     ngx_uint_t                          i, j, k;
  361.     ngx_stream_upstream_chash_point_t  *point;

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

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

  364.     i = 0;
  365.     j = points->number;

  366.     while (i < j) {
  367.         k = (i + j) / 2;

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

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

  372.         } else {
  373.             return k;
  374.         }
  375.     }

  376.     return i;
  377. }


  378. static ngx_int_t
  379. ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,
  380.     ngx_stream_upstream_srv_conf_t *us)
  381. {
  382.     uint32_t                               hash;
  383.     ngx_stream_upstream_hash_srv_conf_t   *hcf;
  384.     ngx_stream_upstream_hash_peer_data_t  *hp;

  385.     if (ngx_stream_upstream_init_hash_peer(s, us) != NGX_OK) {
  386.         return NGX_ERROR;
  387.     }

  388.     s->upstream->peer.get = ngx_stream_upstream_get_chash_peer;

  389.     hp = s->upstream->peer.data;
  390.     hcf = ngx_stream_conf_upstream_srv_conf(us,
  391.                                             ngx_stream_upstream_hash_module);

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

  393.     ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);

  394. #if (NGX_STREAM_UPSTREAM_ZONE)
  395.     if (hp->rrp.peers->config
  396.         && (hcf->points == NULL || hcf->config != *hp->rrp.peers->config))
  397.     {
  398.         if (ngx_stream_upstream_update_chash(NULL, us) != NGX_OK) {
  399.             ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
  400.             return NGX_ERROR;
  401.         }

  402.         hcf->config = *hp->rrp.peers->config;
  403.     }
  404. #endif

  405.     if (hcf->points->number) {
  406.         hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash);
  407.     }

  408.     ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);

  409.     return NGX_OK;
  410. }


  411. static ngx_int_t
  412. ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
  413. {
  414.     ngx_stream_upstream_hash_peer_data_t *hp = data;

  415.     time_t                                now;
  416.     intptr_t                              m;
  417.     ngx_str_t                            *server;
  418.     ngx_int_t                             total;
  419.     ngx_uint_t                            i, n, best_i;
  420.     ngx_stream_upstream_rr_peer_t        *peer, *best;
  421.     ngx_stream_upstream_chash_point_t    *point;
  422.     ngx_stream_upstream_chash_points_t   *points;
  423.     ngx_stream_upstream_hash_srv_conf_t  *hcf;

  424.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
  425.                    "get consistent hash peer, try: %ui", pc->tries);

  426.     ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);

  427.     if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {
  428.         ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
  429.         return hp->get_rr_peer(pc, &hp->rrp);
  430.     }

  431.     pc->connection = NULL;

  432.     if (hp->rrp.peers->number == 0) {
  433.         pc->name = hp->rrp.peers->name;
  434.         ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
  435.         return NGX_BUSY;
  436.     }

  437. #if (NGX_STREAM_UPSTREAM_ZONE)
  438.     if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) {
  439.         pc->name = hp->rrp.peers->name;
  440.         ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
  441.         return NGX_BUSY;
  442.     }
  443. #endif

  444.     now = ngx_time();
  445.     hcf = hp->conf;

  446.     points = hcf->points;
  447.     point = &points->point[0];

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

  450.         ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
  451.                        "consistent hash peer:%uD, server:\"%V\"",
  452.                        hp->hash, server);

  453.         best = NULL;
  454.         best_i = 0;
  455.         total = 0;

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

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

  465.             if (peer->down) {
  466.                 continue;
  467.             }

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

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

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

  483.             peer->current_weight += peer->effective_weight;
  484.             total += peer->effective_weight;

  485.             if (peer->effective_weight < peer->weight) {
  486.                 peer->effective_weight++;
  487.             }

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

  493.         if (best) {
  494.             best->current_weight -= total;
  495.             break;
  496.         }

  497.         hp->hash++;
  498.         hp->tries++;

  499.         if (hp->tries > 20) {
  500.             ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
  501.             return hp->get_rr_peer(pc, &hp->rrp);
  502.         }
  503.     }

  504.     hp->rrp.current = best;
  505.     ngx_stream_upstream_rr_peer_ref(hp->rrp.peers, best);

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

  509.     best->conns++;

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

  513.     ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);

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

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

  517.     return NGX_OK;
  518. }


  519. static void *
  520. ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf)
  521. {
  522.     ngx_stream_upstream_hash_srv_conf_t  *conf;

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

  527.     conf->points = NULL;

  528.     return conf;
  529. }


  530. static char *
  531. ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  532. {
  533.     ngx_stream_upstream_hash_srv_conf_t  *hcf = conf;

  534.     ngx_str_t                           *value;
  535.     ngx_stream_upstream_srv_conf_t      *uscf;
  536.     ngx_stream_compile_complex_value_t   ccv;

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

  538.     ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));

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

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

  545.     uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);

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

  550.     uscf->flags = NGX_STREAM_UPSTREAM_CREATE
  551.                   |NGX_STREAM_UPSTREAM_MODIFY
  552.                   |NGX_STREAM_UPSTREAM_WEIGHT
  553.                   |NGX_STREAM_UPSTREAM_MAX_CONNS
  554.                   |NGX_STREAM_UPSTREAM_MAX_FAILS
  555.                   |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
  556.                   |NGX_STREAM_UPSTREAM_DOWN;

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

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

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

  566.     return NGX_CONF_OK;
  567. }