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


  7. typedef struct {
  8.     ngx_stream_upstream_rr_peer_t          *peer;
  9.     ngx_uint_t                              range;
  10. } ngx_stream_upstream_random_range_t;


  11. typedef struct {
  12.     ngx_uint_t                              two;
  13. #if (NGX_STREAM_UPSTREAM_ZONE)
  14.     ngx_uint_t                              config;
  15. #endif
  16.     ngx_stream_upstream_random_range_t     *ranges;
  17. } ngx_stream_upstream_random_srv_conf_t;


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

  21.     ngx_stream_upstream_random_srv_conf_t  *conf;
  22.     u_char                                  tries;
  23. } ngx_stream_upstream_random_peer_data_t;


  24. static ngx_int_t ngx_stream_upstream_init_random(ngx_conf_t *cf,
  25.     ngx_stream_upstream_srv_conf_t *us);
  26. static ngx_int_t ngx_stream_upstream_update_random(ngx_pool_t *pool,
  27.     ngx_stream_upstream_srv_conf_t *us);

  28. static ngx_int_t ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s,
  29.     ngx_stream_upstream_srv_conf_t *us);
  30. static ngx_int_t ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc,
  31.     void *data);
  32. static ngx_int_t ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc,
  33.     void *data);
  34. static ngx_uint_t ngx_stream_upstream_peek_random_peer(
  35.     ngx_stream_upstream_rr_peers_t *peers,
  36.     ngx_stream_upstream_random_peer_data_t *rp);
  37. static void *ngx_stream_upstream_random_create_conf(ngx_conf_t *cf);
  38. static char *ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd,
  39.     void *conf);


  40. static ngx_command_t  ngx_stream_upstream_random_commands[] = {

  41.     { ngx_string("random"),
  42.       NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12,
  43.       ngx_stream_upstream_random,
  44.       NGX_STREAM_SRV_CONF_OFFSET,
  45.       0,
  46.       NULL },

  47.       ngx_null_command
  48. };


  49. static ngx_stream_module_t  ngx_stream_upstream_random_module_ctx = {
  50.     NULL,                                    /* preconfiguration */
  51.     NULL,                                    /* postconfiguration */

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

  54.     ngx_stream_upstream_random_create_conf/* create server configuration */
  55.     NULL                                     /* merge server configuration */
  56. };


  57. ngx_module_t  ngx_stream_upstream_random_module = {
  58.     NGX_MODULE_V1,
  59.     &ngx_stream_upstream_random_module_ctx/* module context */
  60.     ngx_stream_upstream_random_commands,     /* module directives */
  61.     NGX_STREAM_MODULE,                       /* module type */
  62.     NULL,                                    /* init master */
  63.     NULL,                                    /* init module */
  64.     NULL,                                    /* init process */
  65.     NULL,                                    /* init thread */
  66.     NULL,                                    /* exit thread */
  67.     NULL,                                    /* exit process */
  68.     NULL,                                    /* exit master */
  69.     NGX_MODULE_V1_PADDING
  70. };


  71. static ngx_int_t
  72. ngx_stream_upstream_init_random(ngx_conf_t *cf,
  73.     ngx_stream_upstream_srv_conf_t *us)
  74. {
  75.     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0, "init random");

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

  79.     us->peer.init = ngx_stream_upstream_init_random_peer;

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

  85.     return ngx_stream_upstream_update_random(cf->pool, us);
  86. }


  87. static ngx_int_t
  88. ngx_stream_upstream_update_random(ngx_pool_t *pool,
  89.     ngx_stream_upstream_srv_conf_t *us)
  90. {
  91.     size_t                                  size;
  92.     ngx_uint_t                              i, total_weight;
  93.     ngx_stream_upstream_rr_peer_t          *peer;
  94.     ngx_stream_upstream_rr_peers_t         *peers;
  95.     ngx_stream_upstream_random_range_t     *ranges;
  96.     ngx_stream_upstream_random_srv_conf_t  *rcf;

  97.     rcf = ngx_stream_conf_upstream_srv_conf(us,
  98.                                             ngx_stream_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_stream_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_stream_upstream_init_random_peer(ngx_stream_session_t *s,
  120.     ngx_stream_upstream_srv_conf_t *us)
  121. {
  122.     ngx_stream_upstream_random_srv_conf_t   *rcf;
  123.     ngx_stream_upstream_random_peer_data_t  *rp;

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

  126.     rcf = ngx_stream_conf_upstream_srv_conf(us,
  127.                                             ngx_stream_upstream_random_module);

  128.     rp = ngx_palloc(s->connection->pool,
  129.                     sizeof(ngx_stream_upstream_random_peer_data_t));
  130.     if (rp == NULL) {
  131.         return NGX_ERROR;
  132.     }

  133.     s->upstream->peer.data = &rp->rrp;

  134.     if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
  135.         return NGX_ERROR;
  136.     }

  137.     if (rcf->two) {
  138.         s->upstream->peer.get = ngx_stream_upstream_get_random2_peer;

  139.     } else {
  140.         s->upstream->peer.get = ngx_stream_upstream_get_random_peer;
  141.     }

  142.     rp->conf = rcf;
  143.     rp->tries = 0;

  144.     ngx_stream_upstream_rr_peers_rlock(rp->rrp.peers);

  145. #if (NGX_STREAM_UPSTREAM_ZONE)
  146.     if (rp->rrp.peers->config
  147.         && (rcf->ranges == NULL || rcf->config != *rp->rrp.peers->config))
  148.     {
  149.         if (ngx_stream_upstream_update_random(NULL, us) != NGX_OK) {
  150.             ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers);
  151.             return NGX_ERROR;
  152.         }

  153.         rcf->config = *rp->rrp.peers->config;
  154.     }
  155. #endif

  156.     ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers);

  157.     return NGX_OK;
  158. }


  159. static ngx_int_t
  160. ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data)
  161. {
  162.     ngx_stream_upstream_random_peer_data_t  *rp = data;

  163.     time_t                               now;
  164.     uintptr_t                            m;
  165.     ngx_uint_t                           i, n;
  166.     ngx_stream_upstream_rr_peer_t       *peer;
  167.     ngx_stream_upstream_rr_peers_t      *peers;
  168.     ngx_stream_upstream_rr_peer_data_t  *rrp;

  169.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
  170.                    "get random peer, try: %ui", pc->tries);

  171.     rrp = &rp->rrp;
  172.     peers = rrp->peers;

  173.     ngx_stream_upstream_rr_peers_rlock(peers);

  174.     if (rp->tries > 20 || peers->number < 2) {
  175.         ngx_stream_upstream_rr_peers_unlock(peers);
  176.         return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
  177.     }

  178. #if (NGX_STREAM_UPSTREAM_ZONE)
  179.     if (peers->config && rrp->config != *peers->config) {
  180.         ngx_stream_upstream_rr_peers_unlock(peers);
  181.         return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
  182.     }
  183. #endif

  184.     pc->cached = 0;
  185.     pc->connection = NULL;

  186.     now = ngx_time();

  187.     for ( ;; ) {

  188.         i = ngx_stream_upstream_peek_random_peer(peers, rp);

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

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

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

  195.         ngx_stream_upstream_rr_peer_lock(peers, peer);

  196.         if (peer->down) {
  197.             ngx_stream_upstream_rr_peer_unlock(peers, peer);
  198.             goto next;
  199.         }

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

  207.         if (peer->max_conns && peer->conns >= peer->max_conns) {
  208.             ngx_stream_upstream_rr_peer_unlock(peers, peer);
  209.             goto next;
  210.         }

  211.         break;

  212.     next:

  213.         if (++rp->tries > 20) {
  214.             ngx_stream_upstream_rr_peers_unlock(peers);
  215.             return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
  216.         }
  217.     }

  218.     rrp->current = peer;
  219.     ngx_stream_upstream_rr_peer_ref(peers, peer);

  220.     if (now - peer->checked > peer->fail_timeout) {
  221.         peer->checked = now;
  222.     }

  223.     pc->sockaddr = peer->sockaddr;
  224.     pc->socklen = peer->socklen;
  225.     pc->name = &peer->name;

  226.     peer->conns++;

  227.     ngx_stream_upstream_rr_peer_unlock(peers, peer);
  228.     ngx_stream_upstream_rr_peers_unlock(peers);

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

  230.     return NGX_OK;
  231. }


  232. static ngx_int_t
  233. ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)
  234. {
  235.     ngx_stream_upstream_random_peer_data_t  *rp = data;

  236.     time_t                               now;
  237.     uintptr_t                            m;
  238.     ngx_uint_t                           i, n, p;
  239.     ngx_stream_upstream_rr_peer_t       *peer, *prev;
  240.     ngx_stream_upstream_rr_peers_t      *peers;
  241.     ngx_stream_upstream_rr_peer_data_t  *rrp;

  242.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
  243.                    "get random2 peer, try: %ui", pc->tries);

  244.     rrp = &rp->rrp;
  245.     peers = rrp->peers;

  246.     ngx_stream_upstream_rr_peers_wlock(peers);

  247.     if (rp->tries > 20 || peers->number < 2) {
  248.         ngx_stream_upstream_rr_peers_unlock(peers);
  249.         return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
  250.     }

  251. #if (NGX_STREAM_UPSTREAM_ZONE)
  252.     if (peers->config && rrp->config != *peers->config) {
  253.         ngx_stream_upstream_rr_peers_unlock(peers);
  254.         return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
  255.     }
  256. #endif

  257.     pc->cached = 0;
  258.     pc->connection = NULL;

  259.     now = ngx_time();

  260.     prev = NULL;

  261. #if (NGX_SUPPRESS_WARN)
  262.     p = 0;
  263. #endif

  264.     for ( ;; ) {

  265.         i = ngx_stream_upstream_peek_random_peer(peers, rp);

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

  267.         if (peer == prev) {
  268.             goto next;
  269.         }

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

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

  275.         if (peer->down) {
  276.             goto next;
  277.         }

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

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

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

  293.             break;
  294.         }

  295.         prev = peer;
  296.         p = i;

  297.     next:

  298.         if (++rp->tries > 20) {
  299.             ngx_stream_upstream_rr_peers_unlock(peers);
  300.             return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
  301.         }
  302.     }

  303.     rrp->current = peer;
  304.     ngx_stream_upstream_rr_peer_ref(peers, peer);

  305.     if (now - peer->checked > peer->fail_timeout) {
  306.         peer->checked = now;
  307.     }

  308.     pc->sockaddr = peer->sockaddr;
  309.     pc->socklen = peer->socklen;
  310.     pc->name = &peer->name;

  311.     peer->conns++;

  312.     ngx_stream_upstream_rr_peers_unlock(peers);

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

  314.     return NGX_OK;
  315. }


  316. static ngx_uint_t
  317. ngx_stream_upstream_peek_random_peer(ngx_stream_upstream_rr_peers_t *peers,
  318.     ngx_stream_upstream_random_peer_data_t *rp)
  319. {
  320.     ngx_uint_t  i, j, k, x;

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

  322.     i = 0;
  323.     j = peers->number;

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

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

  328.         } else {
  329.             i = k;
  330.         }
  331.     }

  332.     return i;
  333. }


  334. static void *
  335. ngx_stream_upstream_random_create_conf(ngx_conf_t *cf)
  336. {
  337.     ngx_stream_upstream_random_srv_conf_t  *conf;

  338.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_random_srv_conf_t));
  339.     if (conf == NULL) {
  340.         return NULL;
  341.     }

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

  347.     return conf;
  348. }


  349. static char *
  350. ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  351. {
  352.     ngx_stream_upstream_random_srv_conf_t  *rcf = conf;

  353.     ngx_str_t                       *value;
  354.     ngx_stream_upstream_srv_conf_t  *uscf;

  355.     uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);

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

  360.     uscf->peer.init_upstream = ngx_stream_upstream_init_random;

  361.     uscf->flags = NGX_STREAM_UPSTREAM_CREATE
  362.                   |NGX_STREAM_UPSTREAM_MODIFY
  363.                   |NGX_STREAM_UPSTREAM_WEIGHT
  364.                   |NGX_STREAM_UPSTREAM_MAX_CONNS
  365.                   |NGX_STREAM_UPSTREAM_MAX_FAILS
  366.                   |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
  367.                   |NGX_STREAM_UPSTREAM_DOWN;

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

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

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

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

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

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

  387.     return NGX_CONF_OK;
  388. }