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

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.     ngx_http_upstream_rr_peers_rlock(iphp->rrp.peers);

  119.     if (iphp->tries > 20 || iphp->rrp.peers->number < 2) {
  120.         ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
  121.         return iphp->get_rr_peer(pc, &iphp->rrp);
  122.     }

  123. #if (NGX_HTTP_UPSTREAM_ZONE)
  124.     if (iphp->rrp.peers->config && iphp->rrp.config != *iphp->rrp.peers->config)
  125.     {
  126.         ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
  127.         return iphp->get_rr_peer(pc, &iphp->rrp);
  128.     }
  129. #endif

  130.     now = ngx_time();

  131.     pc->cached = 0;
  132.     pc->connection = NULL;

  133.     hash = iphp->hash;

  134.     for ( ;; ) {

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

  138.         w = hash % iphp->rrp.peers->total_weight;
  139.         peer = iphp->rrp.peers->peer;
  140.         p = 0;

  141.         while (w >= peer->weight) {
  142.             w -= peer->weight;
  143.             peer = peer->next;
  144.             p++;
  145.         }

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

  148.         if (iphp->rrp.tried[n] & m) {
  149.             goto next;
  150.         }

  151.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  152.                        "get ip hash peer, hash: %ui %04XL", p, (uint64_t) m);

  153.         ngx_http_upstream_rr_peer_lock(iphp->rrp.peers, peer);

  154.         if (peer->down) {
  155.             ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
  156.             goto next;
  157.         }

  158.         if (peer->max_fails
  159.             && peer->fails >= peer->max_fails
  160.             && now - peer->checked <= peer->fail_timeout)
  161.         {
  162.             ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
  163.             goto next;
  164.         }

  165.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  166.             ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
  167.             goto next;
  168.         }

  169.         break;

  170.     next:

  171.         if (++iphp->tries > 20) {
  172.             ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
  173.             return iphp->get_rr_peer(pc, &iphp->rrp);
  174.         }
  175.     }

  176.     iphp->rrp.current = peer;
  177.     ngx_http_upstream_rr_peer_ref(iphp->rrp.peers, peer);

  178.     pc->sockaddr = peer->sockaddr;
  179.     pc->socklen = peer->socklen;
  180.     pc->name = &peer->name;

  181.     peer->conns++;

  182.     if (now - peer->checked > peer->fail_timeout) {
  183.         peer->checked = now;
  184.     }

  185.     ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
  186.     ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);

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

  189.     return NGX_OK;
  190. }


  191. static char *
  192. ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  193. {
  194.     ngx_http_upstream_srv_conf_t  *uscf;

  195.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  196.     if (uscf->peer.init_upstream) {
  197.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  198.                            "load balancing method redefined");
  199.     }

  200.     uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;

  201.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  202.                   |NGX_HTTP_UPSTREAM_MODIFY
  203.                   |NGX_HTTP_UPSTREAM_WEIGHT
  204.                   |NGX_HTTP_UPSTREAM_MAX_CONNS
  205.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  206.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  207.                   |NGX_HTTP_UPSTREAM_DOWN;

  208.     return NGX_CONF_OK;
  209. }