src/http/modules/ngx_http_upstream_random_module.c - nginx

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. #if (NGX_HTTP_UPSTREAM_SID)
  186.     peer = ngx_http_upstream_get_rr_peer_by_sid(rrp, pc->hint, &i, 1);

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

  190.         goto found;
  191.     }
  192. #endif

  193.     for ( ;; ) {

  194.         i = ngx_http_upstream_peek_random_peer(peers, rp);

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

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

  198.         if (rrp->tried[n] & m) {
  199.             goto next;
  200.         }

  201.         ngx_http_upstream_rr_peer_lock(peers, peer);

  202.         if (peer->down) {
  203.             ngx_http_upstream_rr_peer_unlock(peers, peer);
  204.             goto next;
  205.         }

  206.         if (peer->max_fails
  207.             && peer->fails >= peer->max_fails
  208.             && now - peer->checked <= peer->fail_timeout)
  209.         {
  210.             ngx_http_upstream_rr_peer_unlock(peers, peer);
  211.             goto next;
  212.         }

  213.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  214.             ngx_http_upstream_rr_peer_unlock(peers, peer);
  215.             goto next;
  216.         }

  217.         break;

  218.     next:

  219.         if (++rp->tries > 20) {
  220.             ngx_http_upstream_rr_peers_unlock(peers);
  221.             return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  222.         }
  223.     }

  224. #if (NGX_HTTP_UPSTREAM_SID)
  225. found:
  226. #endif

  227.     rrp->current = peer;
  228.     ngx_http_upstream_rr_peer_ref(peers, peer);

  229.     if (now - peer->checked > peer->fail_timeout) {
  230.         peer->checked = now;
  231.     }

  232.     pc->sockaddr = peer->sockaddr;
  233.     pc->socklen = peer->socklen;
  234.     pc->name = &peer->name;

  235. #if (NGX_HTTP_UPSTREAM_SID)
  236.     pc->sid = &peer->sid;
  237. #endif

  238.     peer->conns++;

  239.     ngx_http_upstream_rr_peer_unlock(peers, peer);
  240.     ngx_http_upstream_rr_peers_unlock(peers);

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

  242.     return NGX_OK;
  243. }


  244. static ngx_int_t
  245. ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)
  246. {
  247.     ngx_http_upstream_random_peer_data_t  *rp = data;

  248.     time_t                             now;
  249.     uintptr_t                          m;
  250.     ngx_uint_t                         i, n, p;
  251.     ngx_http_upstream_rr_peer_t       *peer, *prev;
  252.     ngx_http_upstream_rr_peers_t      *peers;
  253.     ngx_http_upstream_rr_peer_data_t  *rrp;

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

  256.     rrp = &rp->rrp;
  257.     peers = rrp->peers;

  258.     ngx_http_upstream_rr_peers_wlock(peers);

  259.     if (rp->tries > 20 || peers->number < 2) {
  260.         ngx_http_upstream_rr_peers_unlock(peers);
  261.         return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  262.     }

  263. #if (NGX_HTTP_UPSTREAM_ZONE)
  264.     if (peers->config && rrp->config != *peers->config) {
  265.         ngx_http_upstream_rr_peers_unlock(peers);
  266.         return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  267.     }
  268. #endif

  269.     pc->cached = 0;
  270.     pc->connection = NULL;

  271.     now = ngx_time();

  272.     prev = NULL;

  273. #if (NGX_SUPPRESS_WARN)
  274.     p = 0;
  275. #endif

  276. #if (NGX_HTTP_UPSTREAM_SID)
  277.     peer = ngx_http_upstream_get_rr_peer_by_sid(rrp, pc->hint, &i, 0);

  278.     if (peer) {
  279.         n = i / (8 * sizeof(uintptr_t));
  280.         m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

  281.         goto found;
  282.     }
  283. #endif

  284.     for ( ;; ) {

  285.         i = ngx_http_upstream_peek_random_peer(peers, rp);

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

  287.         if (peer == prev) {
  288.             goto next;
  289.         }

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

  292.         if (rrp->tried[n] & m) {
  293.             goto next;
  294.         }

  295.         if (peer->down) {
  296.             goto next;
  297.         }

  298.         if (peer->max_fails
  299.             && peer->fails >= peer->max_fails
  300.             && now - peer->checked <= peer->fail_timeout)
  301.         {
  302.             goto next;
  303.         }

  304.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  305.             goto next;
  306.         }

  307.         if (prev) {
  308.             if (peer->conns * prev->weight > prev->conns * peer->weight) {
  309.                 peer = prev;
  310.                 n = p / (8 * sizeof(uintptr_t));
  311.                 m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
  312.             }

  313.             break;
  314.         }

  315.         prev = peer;
  316.         p = i;

  317.     next:

  318.         if (++rp->tries > 20) {
  319.             ngx_http_upstream_rr_peers_unlock(peers);
  320.             return ngx_http_upstream_get_round_robin_peer(pc, rrp);
  321.         }
  322.     }

  323. #if (NGX_HTTP_UPSTREAM_SID)
  324. found:
  325. #endif

  326.     rrp->current = peer;
  327.     ngx_http_upstream_rr_peer_ref(peers, peer);

  328.     if (now - peer->checked > peer->fail_timeout) {
  329.         peer->checked = now;
  330.     }

  331.     pc->sockaddr = peer->sockaddr;
  332.     pc->socklen = peer->socklen;
  333.     pc->name = &peer->name;

  334. #if (NGX_HTTP_UPSTREAM_SID)
  335.     pc->sid = &peer->sid;
  336. #endif

  337.     peer->conns++;

  338.     ngx_http_upstream_rr_peers_unlock(peers);

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

  340.     return NGX_OK;
  341. }


  342. static ngx_uint_t
  343. ngx_http_upstream_peek_random_peer(ngx_http_upstream_rr_peers_t *peers,
  344.     ngx_http_upstream_random_peer_data_t *rp)
  345. {
  346.     ngx_uint_t  i, j, k, x;

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

  348.     i = 0;
  349.     j = peers->number;

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

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

  354.         } else {
  355.             i = k;
  356.         }
  357.     }

  358.     return i;
  359. }


  360. static void *
  361. ngx_http_upstream_random_create_conf(ngx_conf_t *cf)
  362. {
  363.     ngx_http_upstream_random_srv_conf_t  *conf;

  364.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_random_srv_conf_t));
  365.     if (conf == NULL) {
  366.         return NULL;
  367.     }

  368.     /*
  369.      * set by ngx_pcalloc():
  370.      *
  371.      *     conf->two = 0;
  372.      */

  373.     return conf;
  374. }


  375. static char *
  376. ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  377. {
  378.     ngx_http_upstream_random_srv_conf_t  *rcf = conf;

  379.     ngx_str_t                     *value;
  380.     ngx_http_upstream_srv_conf_t  *uscf;

  381.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  382.     if (uscf->peer.init_upstream) {
  383.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  384.                            "load balancing method redefined");
  385.     }

  386.     uscf->peer.init_upstream = ngx_http_upstream_init_random;

  387.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  388.                   |NGX_HTTP_UPSTREAM_MODIFY
  389.                   |NGX_HTTP_UPSTREAM_WEIGHT
  390.                   |NGX_HTTP_UPSTREAM_MAX_CONNS
  391.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  392.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  393.                   |NGX_HTTP_UPSTREAM_DOWN;

  394.     if (cf->args->nelts == 1) {
  395.         return NGX_CONF_OK;
  396.     }

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

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

  400.     } else {
  401.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  402.                            "invalid parameter \"%V\"", &value[1]);
  403.         return NGX_CONF_ERROR;
  404.     }

  405.     if (cf->args->nelts == 2) {
  406.         return NGX_CONF_OK;
  407.     }

  408.     if (ngx_strcmp(value[2].data, "least_conn") != 0) {
  409.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  410.                            "invalid parameter \"%V\"", &value[2]);
  411.         return NGX_CONF_ERROR;
  412.     }

  413.     return NGX_CONF_OK;
  414. }