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
13 static ngx_int_t ngx_stream_upstream_init_least_conn_peer(
14 ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us);
15 static ngx_int_t ngx_stream_upstream_get_least_conn_peer(
16 ngx_peer_connection_t *pc, void *data);
17 static char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
18 void *conf);
19
20
21 static 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
34 static 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
46 ngx_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
62 static ngx_int_t
ngx_stream_upstream_init_least_conn(ngx_conf_t * cf,ngx_stream_upstream_srv_conf_t * us)63 ngx_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
79 static ngx_int_t
ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t * s,ngx_stream_upstream_srv_conf_t * us)80 ngx_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
96 static ngx_int_t
ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t * pc,void * data)97 ngx_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
253 failed:
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
287 static char *
ngx_stream_upstream_least_conn(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)288 ngx_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