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

Global variables defined

Data types defined

Functions defined

Source code


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


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


  7. typedef struct {
  8.     ngx_http_upstream_rr_peer_t          *peer;
  9.     ngx_uint_t                            range;
  10. } ngx_http_upstream_random_range_t;


  11. typedef struct {
  12.     ngx_uint_t                            two;
  13. #if (NGX_HTTP_UPSTREAM_ZONE)
  14.     ngx_uint_t                            config;
  15. #endif
  16.     ngx_http_upstream_random_range_t     *ranges;
  17. } ngx_http_upstream_random_srv_conf_t;


  18. typedef struct {
  19.     /* the round robin data must be first */
  20.     ngx_http_upstream_rr_peer_data_t      rrp;

  21.     ngx_http_upstream_random_srv_conf_t  *conf;
  22.     u_char                                tries;
  23. } ngx_http_upstream_random_peer_data_t;


  24. static ngx_int_t ngx_http_upstream_init_random(ngx_conf_t *cf,
  25.     ngx_http_upstream_srv_conf_t *us);
  26. static ngx_int_t ngx_http_upstream_update_random(ngx_pool_t *pool,
  27.     ngx_http_upstream_srv_conf_t *us);

  28. static ngx_int_t ngx_http_upstream_init_random_peer(ngx_http_request_t *r,
  29.     ngx_http_upstream_srv_conf_t *us);
  30. static ngx_int_t ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc,
  31.     void *data);
  32. static ngx_int_t ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc,
  33.     void *data);
  34. static ngx_uint_t ngx_http_upstream_peek_random_peer(
  35.     ngx_http_upstream_rr_peers_t *peers,
  36.     ngx_http_upstream_random_peer_data_t *rp);
  37. static void *ngx_http_upstream_random_create_conf(ngx_conf_t *cf);
  38. static char *ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd,
  39.     void *conf);


  40. static ngx_command_t  ngx_http_upstream_random_commands[] = {

  41.     { ngx_string("random"),
  42.       NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12,
  43.       ngx_http_upstream_random,
  44.       NGX_HTTP_SRV_CONF_OFFSET,
  45.       0,
  46.       NULL },

  47.       ngx_null_command
  48. };


  49. static ngx_http_module_t  ngx_http_upstream_random_module_ctx = {
  50.     NULL,                                  /* preconfiguration */
  51.     NULL,                                  /* postconfiguration */

  52.     NULL,                                  /* create main configuration */
  53.     NULL,                                  /* init main configuration */

  54.     ngx_http_upstream_random_create_conf/* create server configuration */
  55.     NULL,                                  /* merge server configuration */

  56.     NULL,                                  /* create location configuration */
  57.     NULL                                   /* merge location configuration */
  58. };


  59. ngx_module_t  ngx_http_upstream_random_module = {
  60.     NGX_MODULE_V1,
  61.     &ngx_http_upstream_random_module_ctx/* module context */
  62.     ngx_http_upstream_random_commands,     /* module directives */
  63.     NGX_HTTP_MODULE,                       /* module type */
  64.     NULL,                                  /* init master */
  65.     NULL,                                  /* init module */
  66.     NULL,                                  /* init process */
  67.     NULL,                                  /* init thread */
  68.     NULL,                                  /* exit thread */
  69.     NULL,                                  /* exit process */
  70.     NULL,                                  /* exit master */
  71.     NGX_MODULE_V1_PADDING
  72. };


  73. static ngx_int_t
  74. ngx_http_upstream_init_random(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
  75. {
  76.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "init random");

  77.     if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
  78.         return NGX_ERROR;
  79.     }

  80.     us->peer.init = ngx_http_upstream_init_random_peer;

  81. #if (NGX_HTTP_UPSTREAM_ZONE)
  82.     if (us->shm_zone) {
  83.         return NGX_OK;
  84.     }
  85. #endif

  86.     return ngx_http_upstream_update_random(cf->pool, us);
  87. }


  88. static ngx_int_t
  89. ngx_http_upstream_update_random(ngx_pool_t *pool,
  90.     ngx_http_upstream_srv_conf_t *us)
  91. {
  92.     size_t                                size;
  93.     ngx_uint_t                            i, total_weight;
  94.     ngx_http_upstream_rr_peer_t          *peer;
  95.     ngx_http_upstream_rr_peers_t         *peers;
  96.     ngx_http_upstream_random_range_t     *ranges;
  97.     ngx_http_upstream_random_srv_conf_t  *rcf;

  98.     rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module);

  99.     if (rcf->ranges) {
  100.         ngx_free(rcf->ranges);
  101.         rcf->ranges = NULL;
  102.     }

  103.     peers = us->peer.data;

  104.     size = peers->number * sizeof(ngx_http_upstream_random_range_t);

  105.     ranges = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);
  106.     if (ranges == NULL) {
  107.         return NGX_ERROR;
  108.     }

  109.     total_weight = 0;

  110.     for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {
  111.         ranges[i].peer = peer;
  112.         ranges[i].range = total_weight;
  113.         total_weight += peer->weight;
  114.     }

  115.     rcf->ranges = ranges;

  116.     return NGX_OK;
  117. }


  118. static ngx_int_t
  119. ngx_http_upstream_init_random_peer(ngx_http_request_t *r,
  120.     ngx_http_upstream_srv_conf_t *us)
  121. {
  122.     ngx_http_upstream_random_srv_conf_t   *rcf;
  123.     ngx_http_upstream_random_peer_data_t  *rp;

  124.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  125.                    "init random peer");

  126.     rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module);

  127.     rp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_random_peer_data_t));
  128.     if (rp == NULL) {
  129.         return NGX_ERROR;
  130.     }

  131.     r->upstream->peer.data = &rp->rrp;

  132.     if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
  133.         return NGX_ERROR;
  134.     }

  135.     if (rcf->two) {
  136.         r->upstream->peer.get = ngx_http_upstream_get_random2_peer;

  137.     } else {
  138.         r->upstream->peer.get = ngx_http_upstream_get_random_peer;
  139.     }

  140.     rp->conf = rcf;
  141.     rp->tries = 0;

  142.     ngx_http_upstream_rr_peers_rlock(rp->rrp.peers);

  143. #if (NGX_HTTP_UPSTREAM_ZONE)
  144.     if (rp->rrp.peers->config
  145.         && (rcf->ranges == NULL || rcf->config != *rp->rrp.peers->config))
  146.     {
  147.         if (ngx_http_upstream_update_random(NULL, us) != NGX_OK) {
  148.             ngx_http_upstream_rr_peers_unlock(rp->rrp.peers);
  149.             return NGX_ERROR;
  150.         }

  151.         rcf->config = *rp->rrp.peers->config;
  152.     }
  153. #endif

  154.     ngx_http_upstream_rr_peers_unlock(rp->rrp.peers);

  155.     return NGX_OK;
  156. }


  157. static ngx_int_t
  158. ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data)
  159. {
  160.     ngx_http_upstream_random_peer_data_t  *rp = data;

  161.     time_t                             now;
  162.     uintptr_t                          m;
  163.     ngx_uint_t                         i, n;
  164.     ngx_http_upstream_rr_peer_t       *peer;
  165.     ngx_http_upstream_rr_peers_t      *peers;
  166.     ngx_http_upstream_rr_peer_data_t  *rrp;

  167.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  168.                    "get random peer, try: %ui", pc->tries);

  169.     rrp = &rp->rrp;
  170.     peers = rrp->peers;

  171.     ngx_http_upstream_rr_peers_rlock(peers);

  172.     if (rp->tries > 20 || peers->number < 2) {
  173.         ngx_http_upstream_rr_peers_unlock(peers);
  174.         return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  175.     }

  176. #if (NGX_HTTP_UPSTREAM_ZONE)
  177.     if (peers->config && rrp->config != *peers->config) {
  178.         ngx_http_upstream_rr_peers_unlock(peers);
  179.         return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  180.     }
  181. #endif

  182.     pc->cached = 0;
  183.     pc->connection = NULL;

  184.     now = ngx_time();

  185.     for ( ;; ) {

  186.         i = ngx_http_upstream_peek_random_peer(peers, rp);

  187.         peer = rp->conf->ranges[i].peer;

  188.         n = i / (8 * sizeof(uintptr_t));
  189.         m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

  190.         if (rrp->tried[n] & m) {
  191.             goto next;
  192.         }

  193.         ngx_http_upstream_rr_peer_lock(peers, peer);

  194.         if (peer->down) {
  195.             ngx_http_upstream_rr_peer_unlock(peers, peer);
  196.             goto next;
  197.         }

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

  205.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  206.             ngx_http_upstream_rr_peer_unlock(peers, peer);
  207.             goto next;
  208.         }

  209.         break;

  210.     next:

  211.         if (++rp->tries > 20) {
  212.             ngx_http_upstream_rr_peers_unlock(peers);
  213.             return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  214.         }
  215.     }

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

  218.     if (now - peer->checked > peer->fail_timeout) {
  219.         peer->checked = now;
  220.     }

  221.     pc->sockaddr = peer->sockaddr;
  222.     pc->socklen = peer->socklen;
  223.     pc->name = &peer->name;

  224.     peer->conns++;

  225.     ngx_http_upstream_rr_peer_unlock(peers, peer);
  226.     ngx_http_upstream_rr_peers_unlock(peers);

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

  228.     return NGX_OK;
  229. }


  230. static ngx_int_t
  231. ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)
  232. {
  233.     ngx_http_upstream_random_peer_data_t  *rp = data;

  234.     time_t                             now;
  235.     uintptr_t                          m;
  236.     ngx_uint_t                         i, n, p;
  237.     ngx_http_upstream_rr_peer_t       *peer, *prev;
  238.     ngx_http_upstream_rr_peers_t      *peers;
  239.     ngx_http_upstream_rr_peer_data_t  *rrp;

  240.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  241.                    "get random2 peer, try: %ui", pc->tries);

  242.     rrp = &rp->rrp;
  243.     peers = rrp->peers;

  244.     ngx_http_upstream_rr_peers_wlock(peers);

  245.     if (rp->tries > 20 || peers->number < 2) {
  246.         ngx_http_upstream_rr_peers_unlock(peers);
  247.         return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  248.     }

  249. #if (NGX_HTTP_UPSTREAM_ZONE)
  250.     if (peers->config && rrp->config != *peers->config) {
  251.         ngx_http_upstream_rr_peers_unlock(peers);
  252.         return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  253.     }
  254. #endif

  255.     pc->cached = 0;
  256.     pc->connection = NULL;

  257.     now = ngx_time();

  258.     prev = NULL;

  259. #if (NGX_SUPPRESS_WARN)
  260.     p = 0;
  261. #endif

  262.     for ( ;; ) {

  263.         i = ngx_http_upstream_peek_random_peer(peers, rp);

  264.         peer = rp->conf->ranges[i].peer;

  265.         if (peer == prev) {
  266.             goto next;
  267.         }

  268.         n = i / (8 * sizeof(uintptr_t));
  269.         m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

  270.         if (rrp->tried[n] & m) {
  271.             goto next;
  272.         }

  273.         if (peer->down) {
  274.             goto next;
  275.         }

  276.         if (peer->max_fails
  277.             && peer->fails >= peer->max_fails
  278.             && now - peer->checked <= peer->fail_timeout)
  279.         {
  280.             goto next;
  281.         }

  282.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  283.             goto next;
  284.         }

  285.         if (prev) {
  286.             if (peer->conns * prev->weight > prev->conns * peer->weight) {
  287.                 peer = prev;
  288.                 n = p / (8 * sizeof(uintptr_t));
  289.                 m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
  290.             }

  291.             break;
  292.         }

  293.         prev = peer;
  294.         p = i;

  295.     next:

  296.         if (++rp->tries > 20) {
  297.             ngx_http_upstream_rr_peers_unlock(peers);
  298.             return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  299.         }
  300.     }

  301.     rrp->current = peer;
  302.     ngx_http_upstream_rr_peer_ref(peers, peer);

  303.     if (now - peer->checked > peer->fail_timeout) {
  304.         peer->checked = now;
  305.     }

  306.     pc->sockaddr = peer->sockaddr;
  307.     pc->socklen = peer->socklen;
  308.     pc->name = &peer->name;

  309.     peer->conns++;

  310.     ngx_http_upstream_rr_peers_unlock(peers);

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

  312.     return NGX_OK;
  313. }


  314. static ngx_uint_t
  315. ngx_http_upstream_peek_random_peer(ngx_http_upstream_rr_peers_t *peers,
  316.     ngx_http_upstream_random_peer_data_t *rp)
  317. {
  318.     ngx_uint_t  i, j, k, x;

  319.     x = ngx_random() % peers->total_weight;

  320.     i = 0;
  321.     j = peers->number;

  322.     while (j - i > 1) {
  323.         k = (i + j) / 2;

  324.         if (x < rp->conf->ranges[k].range) {
  325.             j = k;

  326.         } else {
  327.             i = k;
  328.         }
  329.     }

  330.     return i;
  331. }


  332. static void *
  333. ngx_http_upstream_random_create_conf(ngx_conf_t *cf)
  334. {
  335.     ngx_http_upstream_random_srv_conf_t  *conf;

  336.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_random_srv_conf_t));
  337.     if (conf == NULL) {
  338.         return NULL;
  339.     }

  340.     /*
  341.      * set by ngx_pcalloc():
  342.      *
  343.      *     conf->two = 0;
  344.      */

  345.     return conf;
  346. }


  347. static char *
  348. ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  349. {
  350.     ngx_http_upstream_random_srv_conf_t  *rcf = conf;

  351.     ngx_str_t                     *value;
  352.     ngx_http_upstream_srv_conf_t  *uscf;

  353.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  354.     if (uscf->peer.init_upstream) {
  355.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  356.                            "load balancing method redefined");
  357.     }

  358.     uscf->peer.init_upstream = ngx_http_upstream_init_random;

  359.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  360.                   |NGX_HTTP_UPSTREAM_MODIFY
  361.                   |NGX_HTTP_UPSTREAM_WEIGHT
  362.                   |NGX_HTTP_UPSTREAM_MAX_CONNS
  363.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  364.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  365.                   |NGX_HTTP_UPSTREAM_DOWN;

  366.     if (cf->args->nelts == 1) {
  367.         return NGX_CONF_OK;
  368.     }

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

  370.     if (ngx_strcmp(value[1].data, "two") == 0) {
  371.         rcf->two = 1;

  372.     } else {
  373.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  374.                            "invalid parameter \"%V\"", &value[1]);
  375.         return NGX_CONF_ERROR;
  376.     }

  377.     if (cf->args->nelts == 2) {
  378.         return NGX_CONF_OK;
  379.     }

  380.     if (ngx_strcmp(value[2].data, "least_conn") != 0) {
  381.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  382.                            "invalid parameter \"%V\"", &value[2]);
  383.         return NGX_CONF_ERROR;
  384.     }

  385.     return NGX_CONF_OK;
  386. }