3 * Copyright (C) Maxim Dounin
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
15 } ngx_http_upstream_least_conn_conf_t
;
19 /* the round robin data must be first */
20 ngx_http_upstream_rr_peer_data_t rrp
;
24 ngx_event_get_peer_pt get_rr_peer
;
25 ngx_event_free_peer_pt free_rr_peer
;
26 } ngx_http_upstream_lc_peer_data_t
;
29 static ngx_int_t
ngx_http_upstream_init_least_conn_peer(ngx_http_request_t
*r
,
30 ngx_http_upstream_srv_conf_t
*us
);
31 static ngx_int_t
ngx_http_upstream_get_least_conn_peer(
32 ngx_peer_connection_t
*pc
, void *data
);
33 static void ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t
*pc
,
34 void *data
, ngx_uint_t state
);
35 static void *ngx_http_upstream_least_conn_create_conf(ngx_conf_t
*cf
);
36 static char *ngx_http_upstream_least_conn(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
40 static ngx_command_t ngx_http_upstream_least_conn_commands
[] = {
42 { ngx_string("least_conn"),
43 NGX_HTTP_UPS_CONF
|NGX_CONF_NOARGS
,
44 ngx_http_upstream_least_conn
,
53 static ngx_http_module_t ngx_http_upstream_least_conn_module_ctx
= {
54 NULL
, /* preconfiguration */
55 NULL
, /* postconfiguration */
57 NULL
, /* create main configuration */
58 NULL
, /* init main configuration */
60 ngx_http_upstream_least_conn_create_conf
, /* create server configuration */
61 NULL
, /* merge server configuration */
63 NULL
, /* create location configuration */
64 NULL
/* merge location configuration */
68 ngx_module_t ngx_http_upstream_least_conn_module
= {
70 &ngx_http_upstream_least_conn_module_ctx
, /* module context */
71 ngx_http_upstream_least_conn_commands
, /* module directives */
72 NGX_HTTP_MODULE
, /* module type */
73 NULL
, /* init master */
74 NULL
, /* init module */
75 NULL
, /* init process */
76 NULL
, /* init thread */
77 NULL
, /* exit thread */
78 NULL
, /* exit process */
79 NULL
, /* exit master */
85 ngx_http_upstream_init_least_conn(ngx_conf_t
*cf
,
86 ngx_http_upstream_srv_conf_t
*us
)
89 ngx_http_upstream_rr_peers_t
*peers
;
90 ngx_http_upstream_least_conn_conf_t
*lcf
;
92 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, cf
->log
, 0,
95 if (ngx_http_upstream_init_round_robin(cf
, us
) != NGX_OK
) {
99 peers
= us
->peer
.data
;
104 n
+= peers
->next
->number
;
107 lcf
= ngx_http_conf_upstream_srv_conf(us
,
108 ngx_http_upstream_least_conn_module
);
110 lcf
->conns
= ngx_pcalloc(cf
->pool
, sizeof(ngx_uint_t
) * n
);
111 if (lcf
->conns
== NULL
) {
115 us
->peer
.init
= ngx_http_upstream_init_least_conn_peer
;
122 ngx_http_upstream_init_least_conn_peer(ngx_http_request_t
*r
,
123 ngx_http_upstream_srv_conf_t
*us
)
125 ngx_http_upstream_lc_peer_data_t
*lcp
;
126 ngx_http_upstream_least_conn_conf_t
*lcf
;
128 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
129 "init least conn peer");
131 lcf
= ngx_http_conf_upstream_srv_conf(us
,
132 ngx_http_upstream_least_conn_module
);
134 lcp
= ngx_palloc(r
->pool
, sizeof(ngx_http_upstream_lc_peer_data_t
));
139 lcp
->conns
= lcf
->conns
;
141 r
->upstream
->peer
.data
= &lcp
->rrp
;
143 if (ngx_http_upstream_init_round_robin_peer(r
, us
) != NGX_OK
) {
147 r
->upstream
->peer
.get
= ngx_http_upstream_get_least_conn_peer
;
148 r
->upstream
->peer
.free
= ngx_http_upstream_free_least_conn_peer
;
150 lcp
->get_rr_peer
= ngx_http_upstream_get_round_robin_peer
;
151 lcp
->free_rr_peer
= ngx_http_upstream_free_round_robin_peer
;
158 ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t
*pc
, void *data
)
160 ngx_http_upstream_lc_peer_data_t
*lcp
= data
;
165 ngx_uint_t i
, n
, p
, many
;
166 ngx_http_upstream_rr_peer_t
*peer
, *best
;
167 ngx_http_upstream_rr_peers_t
*peers
;
169 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, pc
->log
, 0,
170 "get least conn peer, try: %ui", pc
->tries
);
172 if (lcp
->rrp
.peers
->single
) {
173 return lcp
->get_rr_peer(pc
, &lcp
->rrp
);
177 pc
->connection
= NULL
;
181 peers
= lcp
->rrp
.peers
;
186 #if (NGX_SUPPRESS_WARN)
191 for (i
= 0; i
< peers
->number
; i
++) {
193 n
= i
/ (8 * sizeof(uintptr_t));
194 m
= (uintptr_t) 1 << i
% (8 * sizeof(uintptr_t));
196 if (lcp
->rrp
.tried
[n
] & m
) {
200 peer
= &peers
->peer
[i
];
207 && peer
->fails
>= peer
->max_fails
208 && now
- peer
->checked
<= peer
->fail_timeout
)
214 * select peer with least number of connections; if there are
215 * multiple peers with the same number of connections, select
216 * based on round-robin
220 || lcp
->conns
[i
] * best
->weight
< lcp
->conns
[p
] * peer
->weight
)
226 } else if (lcp
->conns
[i
] * best
->weight
227 == lcp
->conns
[p
] * peer
->weight
)
234 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, pc
->log
, 0,
235 "get least conn peer, no peer found");
241 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, pc
->log
, 0,
242 "get least conn peer, many");
244 for (i
= p
; i
< peers
->number
; i
++) {
246 n
= i
/ (8 * sizeof(uintptr_t));
247 m
= (uintptr_t) 1 << i
% (8 * sizeof(uintptr_t));
249 if (lcp
->rrp
.tried
[n
] & m
) {
253 peer
= &peers
->peer
[i
];
259 if (lcp
->conns
[i
] * best
->weight
!= lcp
->conns
[p
] * peer
->weight
) {
264 && peer
->fails
>= peer
->max_fails
265 && now
- peer
->checked
<= peer
->fail_timeout
)
270 peer
->current_weight
+= peer
->effective_weight
;
271 total
+= peer
->effective_weight
;
273 if (peer
->effective_weight
< peer
->weight
) {
274 peer
->effective_weight
++;
277 if (peer
->current_weight
> best
->current_weight
) {
284 best
->current_weight
-= total
;
287 pc
->sockaddr
= best
->sockaddr
;
288 pc
->socklen
= best
->socklen
;
289 pc
->name
= &best
->name
;
291 lcp
->rrp
.current
= p
;
293 n
= p
/ (8 * sizeof(uintptr_t));
294 m
= (uintptr_t) 1 << p
% (8 * sizeof(uintptr_t));
296 lcp
->rrp
.tried
[n
] |= m
;
299 if (pc
->tries
== 1 && peers
->next
) {
300 pc
->tries
+= peers
->next
->number
;
308 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, pc
->log
, 0,
309 "get least conn peer, backup servers");
311 lcp
->conns
+= peers
->number
;
313 lcp
->rrp
.peers
= peers
->next
;
314 pc
->tries
= lcp
->rrp
.peers
->number
;
316 n
= (lcp
->rrp
.peers
->number
+ (8 * sizeof(uintptr_t) - 1))
317 / (8 * sizeof(uintptr_t));
319 for (i
= 0; i
< n
; i
++) {
320 lcp
->rrp
.tried
[i
] = 0;
323 rc
= ngx_http_upstream_get_least_conn_peer(pc
, lcp
);
325 if (rc
!= NGX_BUSY
) {
330 /* all peers failed, mark them as live for quick recovery */
332 for (i
= 0; i
< peers
->number
; i
++) {
333 peers
->peer
[i
].fails
= 0;
336 pc
->name
= peers
->name
;
343 ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t
*pc
,
344 void *data
, ngx_uint_t state
)
346 ngx_http_upstream_lc_peer_data_t
*lcp
= data
;
348 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, pc
->log
, 0,
349 "free least conn peer %ui %ui", pc
->tries
, state
);
351 if (lcp
->rrp
.peers
->single
) {
352 lcp
->free_rr_peer(pc
, &lcp
->rrp
, state
);
356 lcp
->conns
[lcp
->rrp
.current
]--;
358 lcp
->free_rr_peer(pc
, &lcp
->rrp
, state
);
363 ngx_http_upstream_least_conn_create_conf(ngx_conf_t
*cf
)
365 ngx_http_upstream_least_conn_conf_t
*conf
;
367 conf
= ngx_pcalloc(cf
->pool
,
368 sizeof(ngx_http_upstream_least_conn_conf_t
));
374 * set by ngx_pcalloc():
376 * conf->conns = NULL;
384 ngx_http_upstream_least_conn(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
386 ngx_http_upstream_srv_conf_t
*uscf
;
388 uscf
= ngx_http_conf_get_module_srv_conf(cf
, ngx_http_upstream_module
);
390 if (uscf
->peer
.init_upstream
) {
391 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
392 "load balancing method redefined");
395 uscf
->peer
.init_upstream
= ngx_http_upstream_init_least_conn
;
397 uscf
->flags
= NGX_HTTP_UPSTREAM_CREATE
398 |NGX_HTTP_UPSTREAM_WEIGHT
399 |NGX_HTTP_UPSTREAM_MAX_FAILS
400 |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
401 |NGX_HTTP_UPSTREAM_DOWN
402 |NGX_HTTP_UPSTREAM_BACKUP
;