src/http/modules/ngx_http_upstream_least_conn_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Maxim Dounin
  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.     ngx_uint_t                        *conns;
  10. } ngx_http_upstream_least_conn_conf_t;


  11. typedef struct {
  12.     /* the round robin data must be first */
  13.     ngx_http_upstream_rr_peer_data_t   rrp;

  14.     ngx_uint_t                        *conns;

  15.     ngx_event_get_peer_pt              get_rr_peer;
  16.     ngx_event_free_peer_pt             free_rr_peer;
  17. } ngx_http_upstream_lc_peer_data_t;


  18. static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
  19.     ngx_http_upstream_srv_conf_t *us);
  20. static ngx_int_t ngx_http_upstream_get_least_conn_peer(
  21.     ngx_peer_connection_t *pc, void *data);
  22. static void ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
  23.     void *data, ngx_uint_t state);
  24. static void *ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf);
  25. static char *ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
  26.     void *conf);


  27. static ngx_command_t  ngx_http_upstream_least_conn_commands[] = {

  28.     { ngx_string("least_conn"),
  29.       NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
  30.       ngx_http_upstream_least_conn,
  31.       0,
  32.       0,
  33.       NULL },

  34.       ngx_null_command
  35. };


  36. static ngx_http_module_t  ngx_http_upstream_least_conn_module_ctx = {
  37.     NULL,                                  /* preconfiguration */
  38.     NULL,                                  /* postconfiguration */

  39.     NULL,                                  /* create main configuration */
  40.     NULL,                                  /* init main configuration */

  41.     ngx_http_upstream_least_conn_create_conf, /* create server configuration */
  42.     NULL,                                  /* merge server configuration */

  43.     NULL,                                  /* create location configuration */
  44.     NULL                                   /* merge location configuration */
  45. };


  46. ngx_module_t  ngx_http_upstream_least_conn_module = {
  47.     NGX_MODULE_V1,
  48.     &ngx_http_upstream_least_conn_module_ctx, /* module context */
  49.     ngx_http_upstream_least_conn_commands, /* module directives */
  50.     NGX_HTTP_MODULE,                       /* module type */
  51.     NULL,                                  /* init master */
  52.     NULL,                                  /* init module */
  53.     NULL,                                  /* init process */
  54.     NULL,                                  /* init thread */
  55.     NULL,                                  /* exit thread */
  56.     NULL,                                  /* exit process */
  57.     NULL,                                  /* exit master */
  58.     NGX_MODULE_V1_PADDING
  59. };


  60. static ngx_int_t
  61. ngx_http_upstream_init_least_conn(ngx_conf_t *cf,
  62.     ngx_http_upstream_srv_conf_t *us)
  63. {
  64.     ngx_uint_t                            n;
  65.     ngx_http_upstream_rr_peers_t         *peers;
  66.     ngx_http_upstream_least_conn_conf_t  *lcf;

  67.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
  68.                    "init least conn");

  69.     if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
  70.         return NGX_ERROR;
  71.     }

  72.     peers = us->peer.data;

  73.     n = peers->number;

  74.     if (peers->next) {
  75.         n += peers->next->number;
  76.     }

  77.     lcf = ngx_http_conf_upstream_srv_conf(us,
  78.                                           ngx_http_upstream_least_conn_module);

  79.     lcf->conns = ngx_pcalloc(cf->pool, sizeof(ngx_uint_t) * n);
  80.     if (lcf->conns == NULL) {
  81.         return NGX_ERROR;
  82.     }

  83.     us->peer.init = ngx_http_upstream_init_least_conn_peer;

  84.     return NGX_OK;
  85. }


  86. static ngx_int_t
  87. ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
  88.     ngx_http_upstream_srv_conf_t *us)
  89. {
  90.     ngx_http_upstream_lc_peer_data_t     *lcp;
  91.     ngx_http_upstream_least_conn_conf_t  *lcf;

  92.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  93.                    "init least conn peer");

  94.     lcf = ngx_http_conf_upstream_srv_conf(us,
  95.                                           ngx_http_upstream_least_conn_module);

  96.     lcp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_lc_peer_data_t));
  97.     if (lcp == NULL) {
  98.         return NGX_ERROR;
  99.     }

  100.     lcp->conns = lcf->conns;

  101.     r->upstream->peer.data = &lcp->rrp;

  102.     if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
  103.         return NGX_ERROR;
  104.     }

  105.     r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;
  106.     r->upstream->peer.free = ngx_http_upstream_free_least_conn_peer;

  107.     lcp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
  108.     lcp->free_rr_peer = ngx_http_upstream_free_round_robin_peer;

  109.     return NGX_OK;
  110. }


  111. static ngx_int_t
  112. ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
  113. {
  114.     ngx_http_upstream_lc_peer_data_t  *lcp = data;

  115.     time_t                         now;
  116.     uintptr_t                      m;
  117.     ngx_int_t                      rc, total;
  118.     ngx_uint_t                     i, n, p, many;
  119.     ngx_http_upstream_rr_peer_t   *peer, *best;
  120.     ngx_http_upstream_rr_peers_t  *peers;

  121.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  122.                    "get least conn peer, try: %ui", pc->tries);

  123.     if (lcp->rrp.peers->single) {
  124.         return lcp->get_rr_peer(pc, &lcp->rrp);
  125.     }

  126.     pc->cached = 0;
  127.     pc->connection = NULL;

  128.     now = ngx_time();

  129.     peers = lcp->rrp.peers;

  130.     best = NULL;
  131.     total = 0;

  132. #if (NGX_SUPPRESS_WARN)
  133.     many = 0;
  134.     p = 0;
  135. #endif

  136.     for (i = 0; i < peers->number; i++) {

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

  139.         if (lcp->rrp.tried[n] & m) {
  140.             continue;
  141.         }

  142.         peer = &peers->peer[i];

  143.         if (peer->down) {
  144.             continue;
  145.         }

  146.         if (peer->max_fails
  147.             && peer->fails >= peer->max_fails
  148.             && now - peer->checked <= peer->fail_timeout)
  149.         {
  150.             continue;
  151.         }

  152.         /*
  153.          * select peer with least number of connections; if there are
  154.          * multiple peers with the same number of connections, select
  155.          * based on round-robin
  156.          */

  157.         if (best == NULL
  158.             || lcp->conns[i] * best->weight < lcp->conns[p] * peer->weight)
  159.         {
  160.             best = peer;
  161.             many = 0;
  162.             p = i;

  163.         } else if (lcp->conns[i] * best->weight
  164.                    == lcp->conns[p] * peer->weight)
  165.         {
  166.             many = 1;
  167.         }
  168.     }

  169.     if (best == NULL) {
  170.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  171.                        "get least conn peer, no peer found");

  172.         goto failed;
  173.     }

  174.     if (many) {
  175.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  176.                        "get least conn peer, many");

  177.         for (i = p; i < peers->number; i++) {

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

  180.             if (lcp->rrp.tried[n] & m) {
  181.                 continue;
  182.             }

  183.             peer = &peers->peer[i];

  184.             if (peer->down) {
  185.                 continue;
  186.             }

  187.             if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
  188.                 continue;
  189.             }

  190.             if (peer->max_fails
  191.                 && peer->fails >= peer->max_fails
  192.                 && now - peer->checked <= peer->fail_timeout)
  193.             {
  194.                 continue;
  195.             }

  196.             peer->current_weight += peer->effective_weight;
  197.             total += peer->effective_weight;

  198.             if (peer->effective_weight < peer->weight) {
  199.                 peer->effective_weight++;
  200.             }

  201.             if (peer->current_weight > best->current_weight) {
  202.                 best = peer;
  203.                 p = i;
  204.             }
  205.         }
  206.     }

  207.     best->current_weight -= total;

  208.     if (now - best->checked > best->fail_timeout) {
  209.         best->checked = now;
  210.     }

  211.     pc->sockaddr = best->sockaddr;
  212.     pc->socklen = best->socklen;
  213.     pc->name = &best->name;

  214.     lcp->rrp.current = p;

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

  217.     lcp->rrp.tried[n] |= m;
  218.     lcp->conns[p]++;

  219.     return NGX_OK;

  220. failed:

  221.     if (peers->next) {
  222.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  223.                        "get least conn peer, backup servers");

  224.         lcp->conns += peers->number;

  225.         lcp->rrp.peers = peers->next;

  226.         n = (lcp->rrp.peers->number + (8 * sizeof(uintptr_t) - 1))
  227.                 / (8 * sizeof(uintptr_t));

  228.         for (i = 0; i < n; i++) {
  229.              lcp->rrp.tried[i] = 0;
  230.         }

  231.         rc = ngx_http_upstream_get_least_conn_peer(pc, lcp);

  232.         if (rc != NGX_BUSY) {
  233.             return rc;
  234.         }
  235.     }

  236.     /* all peers failed, mark them as live for quick recovery */

  237.     for (i = 0; i < peers->number; i++) {
  238.         peers->peer[i].fails = 0;
  239.     }

  240.     pc->name = peers->name;

  241.     return NGX_BUSY;
  242. }


  243. static void
  244. ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
  245.     void *data, ngx_uint_t state)
  246. {
  247.     ngx_http_upstream_lc_peer_data_t  *lcp = data;

  248.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
  249.                    "free least conn peer %ui %ui", pc->tries, state);

  250.     if (lcp->rrp.peers->single) {
  251.         lcp->free_rr_peer(pc, &lcp->rrp, state);
  252.         return;
  253.     }

  254.     lcp->conns[lcp->rrp.current]--;

  255.     lcp->free_rr_peer(pc, &lcp->rrp, state);
  256. }


  257. static void *
  258. ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf)
  259. {
  260.     ngx_http_upstream_least_conn_conf_t  *conf;

  261.     conf = ngx_pcalloc(cf->pool,
  262.                        sizeof(ngx_http_upstream_least_conn_conf_t));
  263.     if (conf == NULL) {
  264.         return NULL;
  265.     }

  266.     /*
  267.      * set by ngx_pcalloc():
  268.      *
  269.      *     conf->conns = NULL;
  270.      */

  271.     return conf;
  272. }


  273. static char *
  274. ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  275. {
  276.     ngx_http_upstream_srv_conf_t  *uscf;

  277.     uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);

  278.     if (uscf->peer.init_upstream) {
  279.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  280.                            "load balancing method redefined");
  281.     }

  282.     uscf->peer.init_upstream = ngx_http_upstream_init_least_conn;

  283.     uscf->flags = NGX_HTTP_UPSTREAM_CREATE
  284.                   |NGX_HTTP_UPSTREAM_WEIGHT
  285.                   |NGX_HTTP_UPSTREAM_MAX_FAILS
  286.                   |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
  287.                   |NGX_HTTP_UPSTREAM_DOWN
  288.                   |NGX_HTTP_UPSTREAM_BACKUP;

  289.     return NGX_CONF_OK;
  290. }