ngx_stream_upstream_least_conn_module.c revision e18a033b
1
2/*
3 * Copyright (C) Maxim Dounin
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_stream.h>
11
12
13static ngx_int_t ngx_stream_upstream_init_least_conn_peer(
14    ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us);
15static ngx_int_t ngx_stream_upstream_get_least_conn_peer(
16    ngx_peer_connection_t *pc, void *data);
17static char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
18    void *conf);
19
20
21static ngx_command_t  ngx_stream_upstream_least_conn_commands[] = {
22
23    { ngx_string("least_conn"),
24      NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS,
25      ngx_stream_upstream_least_conn,
26      0,
27      0,
28      NULL },
29
30      ngx_null_command
31};
32
33
34static ngx_stream_module_t  ngx_stream_upstream_least_conn_module_ctx = {
35    NULL,                                    /* preconfiguration */
36    NULL,                                    /* postconfiguration */
37
38    NULL,                                    /* create main configuration */
39    NULL,                                    /* init main configuration */
40
41    NULL,                                    /* create server configuration */
42    NULL                                     /* merge server configuration */
43};
44
45
46ngx_module_t  ngx_stream_upstream_least_conn_module = {
47    NGX_MODULE_V1,
48    &ngx_stream_upstream_least_conn_module_ctx, /* module context */
49    ngx_stream_upstream_least_conn_commands, /* module directives */
50    NGX_STREAM_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
61
62static ngx_int_t
63ngx_stream_upstream_init_least_conn(ngx_conf_t *cf,
64    ngx_stream_upstream_srv_conf_t *us)
65{
66    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0,
67                   "init least conn");
68
69    if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
70        return NGX_ERROR;
71    }
72
73    us->peer.init = ngx_stream_upstream_init_least_conn_peer;
74
75    return NGX_OK;
76}
77
78
79static ngx_int_t
80ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s,
81    ngx_stream_upstream_srv_conf_t *us)
82{
83    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
84                   "init least conn peer");
85
86    if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
87        return NGX_ERROR;
88    }
89
90    s->upstream->peer.get = ngx_stream_upstream_get_least_conn_peer;
91
92    return NGX_OK;
93}
94
95
96static ngx_int_t
97ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
98{
99    ngx_stream_upstream_rr_peer_data_t *rrp = data;
100
101    time_t                           now;
102    uintptr_t                        m;
103    ngx_int_t                        rc, total;
104    ngx_uint_t                       i, n, p, many;
105    ngx_stream_upstream_rr_peer_t   *peer, *best;
106    ngx_stream_upstream_rr_peers_t  *peers;
107
108    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
109                   "get least conn peer, try: %ui", pc->tries);
110
111    if (rrp->peers->single) {
112        return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
113    }
114
115    pc->connection = NULL;
116
117    now = ngx_time();
118
119    peers = rrp->peers;
120
121    ngx_stream_upstream_rr_peers_wlock(peers);
122
123    best = NULL;
124    total = 0;
125
126#if (NGX_SUPPRESS_WARN)
127    many = 0;
128    p = 0;
129#endif
130
131    for (peer = peers->peer, i = 0;
132         peer;
133         peer = peer->next, i++)
134    {
135        n = i / (8 * sizeof(uintptr_t));
136        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
137
138        if (rrp->tried[n] & m) {
139            continue;
140        }
141
142        if (peer->down) {
143            continue;
144        }
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        if (peer->max_conns && peer->conns >= peer->max_conns) {
154            continue;
155        }
156
157        /*
158         * select peer with least number of connections; if there are
159         * multiple peers with the same number of connections, select
160         * based on round-robin
161         */
162
163        if (best == NULL
164            || peer->conns * best->weight < best->conns * peer->weight)
165        {
166            best = peer;
167            many = 0;
168            p = i;
169
170        } else if (peer->conns * best->weight == best->conns * peer->weight) {
171            many = 1;
172        }
173    }
174
175    if (best == NULL) {
176        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
177                       "get least conn peer, no peer found");
178
179        goto failed;
180    }
181
182    if (many) {
183        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
184                       "get least conn peer, many");
185
186        for (peer = best, i = p;
187             peer;
188             peer = peer->next, i++)
189        {
190            n = i / (8 * sizeof(uintptr_t));
191            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
192
193            if (rrp->tried[n] & m) {
194                continue;
195            }
196
197            if (peer->down) {
198                continue;
199            }
200
201            if (peer->conns * best->weight != best->conns * peer->weight) {
202                continue;
203            }
204
205            if (peer->max_fails
206                && peer->fails >= peer->max_fails
207                && now - peer->checked <= peer->fail_timeout)
208            {
209                continue;
210            }
211
212            if (peer->max_conns && peer->conns >= peer->max_conns) {
213                continue;
214            }
215
216            peer->current_weight += peer->effective_weight;
217            total += peer->effective_weight;
218
219            if (peer->effective_weight < peer->weight) {
220                peer->effective_weight++;
221            }
222
223            if (peer->current_weight > best->current_weight) {
224                best = peer;
225                p = i;
226            }
227        }
228    }
229
230    best->current_weight -= total;
231
232    if (now - best->checked > best->fail_timeout) {
233        best->checked = now;
234    }
235
236    pc->sockaddr = best->sockaddr;
237    pc->socklen = best->socklen;
238    pc->name = &best->name;
239
240    best->conns++;
241
242    rrp->current = best;
243
244    n = p / (8 * sizeof(uintptr_t));
245    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
246
247    rrp->tried[n] |= m;
248
249    ngx_stream_upstream_rr_peers_unlock(peers);
250
251    return NGX_OK;
252
253failed:
254
255    if (peers->next) {
256        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
257                       "get least conn peer, backup servers");
258
259        rrp->peers = peers->next;
260
261        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
262                / (8 * sizeof(uintptr_t));
263
264        for (i = 0; i < n; i++) {
265            rrp->tried[i] = 0;
266        }
267
268        ngx_stream_upstream_rr_peers_unlock(peers);
269
270        rc = ngx_stream_upstream_get_least_conn_peer(pc, rrp);
271
272        if (rc != NGX_BUSY) {
273            return rc;
274        }
275
276        ngx_stream_upstream_rr_peers_wlock(peers);
277    }
278
279    ngx_stream_upstream_rr_peers_unlock(peers);
280
281    pc->name = peers->name;
282
283    return NGX_BUSY;
284}
285
286
287static char *
288ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
289{
290    ngx_stream_upstream_srv_conf_t  *uscf;
291
292    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
293
294    if (uscf->peer.init_upstream) {
295        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
296                           "load balancing method redefined");
297    }
298
299    uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn;
300
301    uscf->flags = NGX_STREAM_UPSTREAM_CREATE
302                  |NGX_STREAM_UPSTREAM_WEIGHT
303                  |NGX_STREAM_UPSTREAM_MAX_CONNS
304                  |NGX_STREAM_UPSTREAM_MAX_FAILS
305                  |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
306                  |NGX_STREAM_UPSTREAM_DOWN
307                  |NGX_STREAM_UPSTREAM_BACKUP;
308
309    return NGX_CONF_OK;
310}
311