Mercurial > hg > nginx-quic
annotate src/stream/ngx_stream_upstream_hash_module.c @ 6755:e2f13011343e stable-1.10
HTTP/2: fixed the "http request count is zero" alert.
When the stream is terminated the HEADERS frame can still wait in the output
queue. This frame can't be removed and must be sent to the client anyway,
since HTTP/2 uses stateful compression for headers. So in order to postpone
closing and freeing memory of such stream the special close stream handler
is set to the write event. After the HEADERS frame is sent the write event
is called and the stream will be finally closed.
Some events like receiving a RST_STREAM can trigger the read handler of such
stream in closing state and cause unexpected processing that can result in
another attempt to finalize the request. To prevent it the read handler is
now set to ngx_http_empty_handler.
Thanks to Amazon.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Thu, 16 Jun 2016 20:55:11 +0300 |
parents | 68c106e6fa0a |
children | 2f41d383c9c7 |
rev | line source |
---|---|
6115 | 1 |
2 /* | |
3 * Copyright (C) Roman Arutyunyan | |
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 typedef struct { | |
14 uint32_t hash; | |
15 ngx_str_t *server; | |
16 } ngx_stream_upstream_chash_point_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_uint_t number; | |
21 ngx_stream_upstream_chash_point_t point[1]; | |
22 } ngx_stream_upstream_chash_points_t; | |
23 | |
24 | |
25 typedef struct { | |
26 ngx_stream_upstream_chash_points_t *points; | |
27 } ngx_stream_upstream_hash_srv_conf_t; | |
28 | |
29 | |
30 typedef struct { | |
31 /* the round robin data must be first */ | |
32 ngx_stream_upstream_rr_peer_data_t rrp; | |
33 ngx_stream_upstream_hash_srv_conf_t *conf; | |
34 ngx_str_t key; | |
35 ngx_uint_t tries; | |
36 ngx_uint_t rehash; | |
37 uint32_t hash; | |
38 ngx_event_get_peer_pt get_rr_peer; | |
39 } ngx_stream_upstream_hash_peer_data_t; | |
40 | |
41 | |
42 static ngx_int_t ngx_stream_upstream_init_hash(ngx_conf_t *cf, | |
43 ngx_stream_upstream_srv_conf_t *us); | |
44 static ngx_int_t ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s, | |
45 ngx_stream_upstream_srv_conf_t *us); | |
46 static ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, | |
47 void *data); | |
48 | |
49 static ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf, | |
50 ngx_stream_upstream_srv_conf_t *us); | |
51 static int ngx_libc_cdecl | |
52 ngx_stream_upstream_chash_cmp_points(const void *one, const void *two); | |
53 static ngx_uint_t ngx_stream_upstream_find_chash_point( | |
54 ngx_stream_upstream_chash_points_t *points, uint32_t hash); | |
55 static ngx_int_t ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s, | |
56 ngx_stream_upstream_srv_conf_t *us); | |
57 static ngx_int_t ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, | |
58 void *data); | |
59 | |
60 static void *ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf); | |
61 static char *ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, | |
62 void *conf); | |
63 | |
64 | |
65 static ngx_command_t ngx_stream_upstream_hash_commands[] = { | |
66 | |
67 { ngx_string("hash"), | |
68 NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12, | |
69 ngx_stream_upstream_hash, | |
70 NGX_STREAM_SRV_CONF_OFFSET, | |
71 0, | |
72 NULL }, | |
73 | |
74 ngx_null_command | |
75 }; | |
76 | |
77 | |
78 static ngx_stream_module_t ngx_stream_upstream_hash_module_ctx = { | |
6174
68c106e6fa0a
Stream: added postconfiguration method to stream modules.
Vladimir Homutov <vl@nginx.com>
parents:
6148
diff
changeset
|
79 NULL, /* postconfiguration */ |
68c106e6fa0a
Stream: added postconfiguration method to stream modules.
Vladimir Homutov <vl@nginx.com>
parents:
6148
diff
changeset
|
80 |
6115 | 81 NULL, /* create main configuration */ |
82 NULL, /* init main configuration */ | |
83 | |
84 ngx_stream_upstream_hash_create_conf, /* create server configuration */ | |
85 NULL, /* merge server configuration */ | |
86 }; | |
87 | |
88 | |
89 ngx_module_t ngx_stream_upstream_hash_module = { | |
90 NGX_MODULE_V1, | |
91 &ngx_stream_upstream_hash_module_ctx, /* module context */ | |
92 ngx_stream_upstream_hash_commands, /* module directives */ | |
93 NGX_STREAM_MODULE, /* module type */ | |
94 NULL, /* init master */ | |
95 NULL, /* init module */ | |
96 NULL, /* init process */ | |
97 NULL, /* init thread */ | |
98 NULL, /* exit thread */ | |
99 NULL, /* exit process */ | |
100 NULL, /* exit master */ | |
101 NGX_MODULE_V1_PADDING | |
102 }; | |
103 | |
104 | |
105 static ngx_int_t | |
106 ngx_stream_upstream_init_hash(ngx_conf_t *cf, | |
107 ngx_stream_upstream_srv_conf_t *us) | |
108 { | |
109 if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { | |
110 return NGX_ERROR; | |
111 } | |
112 | |
113 us->peer.init = ngx_stream_upstream_init_hash_peer; | |
114 | |
115 return NGX_OK; | |
116 } | |
117 | |
118 | |
119 static ngx_int_t | |
120 ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s, | |
121 ngx_stream_upstream_srv_conf_t *us) | |
122 { | |
123 ngx_stream_upstream_hash_srv_conf_t *hcf; | |
124 ngx_stream_upstream_hash_peer_data_t *hp; | |
125 | |
126 hp = ngx_palloc(s->connection->pool, | |
127 sizeof(ngx_stream_upstream_hash_peer_data_t)); | |
128 if (hp == NULL) { | |
129 return NGX_ERROR; | |
130 } | |
131 | |
132 s->upstream->peer.data = &hp->rrp; | |
133 | |
134 if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) { | |
135 return NGX_ERROR; | |
136 } | |
137 | |
138 s->upstream->peer.get = ngx_stream_upstream_get_hash_peer; | |
139 | |
140 hcf = ngx_stream_conf_upstream_srv_conf(us, | |
141 ngx_stream_upstream_hash_module); | |
142 | |
143 hp->key = s->connection->addr_text; | |
144 | |
145 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, | |
146 "upstream hash key:\"%V\"", &hp->key); | |
147 | |
148 hp->conf = hcf; | |
149 hp->tries = 0; | |
150 hp->rehash = 0; | |
151 hp->hash = 0; | |
152 hp->get_rr_peer = ngx_stream_upstream_get_round_robin_peer; | |
153 | |
154 return NGX_OK; | |
155 } | |
156 | |
157 | |
158 static ngx_int_t | |
159 ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) | |
160 { | |
161 ngx_stream_upstream_hash_peer_data_t *hp = data; | |
162 | |
163 time_t now; | |
164 u_char buf[NGX_INT_T_LEN]; | |
165 size_t size; | |
166 uint32_t hash; | |
167 ngx_int_t w; | |
168 uintptr_t m; | |
6121
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
169 ngx_uint_t n, p; |
6115 | 170 ngx_stream_upstream_rr_peer_t *peer; |
171 | |
172 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
173 "get hash peer, try: %ui", pc->tries); | |
174 | |
175 ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers); | |
176 | |
177 if (hp->tries > 20 || hp->rrp.peers->single) { | |
178 ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); | |
179 return hp->get_rr_peer(pc, &hp->rrp); | |
180 } | |
181 | |
182 now = ngx_time(); | |
183 | |
184 pc->connection = NULL; | |
185 | |
186 for ( ;; ) { | |
187 | |
188 /* | |
189 * Hash expression is compatible with Cache::Memcached: | |
190 * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH | |
191 * with REHASH omitted at the first iteration. | |
192 */ | |
193 | |
194 ngx_crc32_init(hash); | |
195 | |
196 if (hp->rehash > 0) { | |
197 size = ngx_sprintf(buf, "%ui", hp->rehash) - buf; | |
198 ngx_crc32_update(&hash, buf, size); | |
199 } | |
200 | |
201 ngx_crc32_update(&hash, hp->key.data, hp->key.len); | |
202 ngx_crc32_final(hash); | |
203 | |
204 hash = (hash >> 16) & 0x7fff; | |
205 | |
206 hp->hash += hash; | |
207 hp->rehash++; | |
208 | |
6121
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
209 w = hp->hash % hp->rrp.peers->total_weight; |
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
210 peer = hp->rrp.peers->peer; |
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
211 p = 0; |
6115 | 212 |
6121
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
213 while (w >= peer->weight) { |
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
214 w -= peer->weight; |
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
215 peer = peer->next; |
b6047abf5f30
Upstream: simplified ip_hash and hash peer selection code.
Ruslan Ermilov <ru@nginx.com>
parents:
6115
diff
changeset
|
216 p++; |
6115 | 217 } |
218 | |
219 n = p / (8 * sizeof(uintptr_t)); | |
220 m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); | |
221 | |
222 if (hp->rrp.tried[n] & m) { | |
223 goto next; | |
224 } | |
225 | |
226 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
227 "get hash peer, value:%uD, peer:%ui", hp->hash, p); | |
228 | |
229 if (peer->down) { | |
230 goto next; | |
231 } | |
232 | |
233 if (peer->max_fails | |
234 && peer->fails >= peer->max_fails | |
235 && now - peer->checked <= peer->fail_timeout) | |
236 { | |
237 goto next; | |
238 } | |
239 | |
240 break; | |
241 | |
242 next: | |
243 | |
244 if (++hp->tries > 20) { | |
245 ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); | |
246 return hp->get_rr_peer(pc, &hp->rrp); | |
247 } | |
248 } | |
249 | |
250 hp->rrp.current = peer; | |
251 | |
252 pc->sockaddr = peer->sockaddr; | |
253 pc->socklen = peer->socklen; | |
254 pc->name = &peer->name; | |
255 | |
256 peer->conns++; | |
257 | |
258 if (now - peer->checked > peer->fail_timeout) { | |
259 peer->checked = now; | |
260 } | |
261 | |
262 ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); | |
263 | |
264 hp->rrp.tried[n] |= m; | |
265 | |
266 return NGX_OK; | |
267 } | |
268 | |
269 | |
270 static ngx_int_t | |
271 ngx_stream_upstream_init_chash(ngx_conf_t *cf, | |
272 ngx_stream_upstream_srv_conf_t *us) | |
273 { | |
274 u_char *host, *port, c; | |
275 size_t host_len, port_len, size; | |
6148
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
276 uint32_t hash, base_hash; |
6115 | 277 ngx_str_t *server; |
278 ngx_uint_t npoints, i, j; | |
279 ngx_stream_upstream_rr_peer_t *peer; | |
280 ngx_stream_upstream_rr_peers_t *peers; | |
281 ngx_stream_upstream_chash_points_t *points; | |
282 ngx_stream_upstream_hash_srv_conf_t *hcf; | |
6148
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
283 union { |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
284 uint32_t value; |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
285 u_char byte[4]; |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
286 } prev_hash; |
6115 | 287 |
288 if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { | |
289 return NGX_ERROR; | |
290 } | |
291 | |
292 us->peer.init = ngx_stream_upstream_init_chash_peer; | |
293 | |
294 peers = us->peer.data; | |
295 npoints = peers->total_weight * 160; | |
296 | |
297 size = sizeof(ngx_stream_upstream_chash_points_t) | |
298 + sizeof(ngx_stream_upstream_chash_point_t) * (npoints - 1); | |
299 | |
300 points = ngx_palloc(cf->pool, size); | |
301 if (points == NULL) { | |
302 return NGX_ERROR; | |
303 } | |
304 | |
305 points->number = 0; | |
306 | |
307 for (peer = peers->peer; peer; peer = peer->next) { | |
308 server = &peer->server; | |
309 | |
310 /* | |
311 * Hash expression is compatible with Cache::Memcached::Fast: | |
312 * crc32(HOST \0 PORT PREV_HASH). | |
313 */ | |
314 | |
315 if (server->len >= 5 | |
316 && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0) | |
317 { | |
318 host = server->data + 5; | |
319 host_len = server->len - 5; | |
320 port = NULL; | |
321 port_len = 0; | |
322 goto done; | |
323 } | |
324 | |
325 for (j = 0; j < server->len; j++) { | |
326 c = server->data[server->len - j - 1]; | |
327 | |
328 if (c == ':') { | |
329 host = server->data; | |
330 host_len = server->len - j - 1; | |
331 port = server->data + server->len - j; | |
332 port_len = j; | |
333 goto done; | |
334 } | |
335 | |
336 if (c < '0' || c > '9') { | |
337 break; | |
338 } | |
339 } | |
340 | |
341 host = server->data; | |
342 host_len = server->len; | |
343 port = NULL; | |
344 port_len = 0; | |
345 | |
346 done: | |
347 | |
348 ngx_crc32_init(base_hash); | |
349 ngx_crc32_update(&base_hash, host, host_len); | |
350 ngx_crc32_update(&base_hash, (u_char *) "", 1); | |
351 ngx_crc32_update(&base_hash, port, port_len); | |
352 | |
6148
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
353 prev_hash.value = 0; |
6115 | 354 npoints = peer->weight * 160; |
355 | |
356 for (j = 0; j < npoints; j++) { | |
357 hash = base_hash; | |
358 | |
6148
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
359 ngx_crc32_update(&hash, prev_hash.byte, 4); |
6115 | 360 ngx_crc32_final(hash); |
361 | |
362 points->point[points->number].hash = hash; | |
363 points->point[points->number].server = server; | |
364 points->number++; | |
365 | |
6148
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
366 #if (NGX_HAVE_LITTLE_ENDIAN) |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
367 prev_hash.value = hash; |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
368 #else |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
369 prev_hash.byte[0] = (u_char) (hash & 0xff); |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
370 prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff); |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
371 prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff); |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
372 prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff); |
bf8b6534db3a
Upstream hash: consistency across little/big endianness.
Sergey Kandaurov <pluknet@nginx.com>
parents:
6121
diff
changeset
|
373 #endif |
6115 | 374 } |
375 } | |
376 | |
377 ngx_qsort(points->point, | |
378 points->number, | |
379 sizeof(ngx_stream_upstream_chash_point_t), | |
380 ngx_stream_upstream_chash_cmp_points); | |
381 | |
382 for (i = 0, j = 1; j < points->number; j++) { | |
383 if (points->point[i].hash != points->point[j].hash) { | |
384 points->point[++i] = points->point[j]; | |
385 } | |
386 } | |
387 | |
388 points->number = i + 1; | |
389 | |
390 hcf = ngx_stream_conf_upstream_srv_conf(us, | |
391 ngx_stream_upstream_hash_module); | |
392 hcf->points = points; | |
393 | |
394 return NGX_OK; | |
395 } | |
396 | |
397 | |
398 static int ngx_libc_cdecl | |
399 ngx_stream_upstream_chash_cmp_points(const void *one, const void *two) | |
400 { | |
401 ngx_stream_upstream_chash_point_t *first = | |
402 (ngx_stream_upstream_chash_point_t *) one; | |
403 ngx_stream_upstream_chash_point_t *second = | |
404 (ngx_stream_upstream_chash_point_t *) two; | |
405 | |
406 if (first->hash < second->hash) { | |
407 return -1; | |
408 | |
409 } else if (first->hash > second->hash) { | |
410 return 1; | |
411 | |
412 } else { | |
413 return 0; | |
414 } | |
415 } | |
416 | |
417 | |
418 static ngx_uint_t | |
419 ngx_stream_upstream_find_chash_point(ngx_stream_upstream_chash_points_t *points, | |
420 uint32_t hash) | |
421 { | |
422 ngx_uint_t i, j, k; | |
423 ngx_stream_upstream_chash_point_t *point; | |
424 | |
425 /* find first point >= hash */ | |
426 | |
427 point = &points->point[0]; | |
428 | |
429 i = 0; | |
430 j = points->number; | |
431 | |
432 while (i < j) { | |
433 k = (i + j) / 2; | |
434 | |
435 if (hash > point[k].hash) { | |
436 i = k + 1; | |
437 | |
438 } else if (hash < point[k].hash) { | |
439 j = k; | |
440 | |
441 } else { | |
442 return k; | |
443 } | |
444 } | |
445 | |
446 return i; | |
447 } | |
448 | |
449 | |
450 static ngx_int_t | |
451 ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s, | |
452 ngx_stream_upstream_srv_conf_t *us) | |
453 { | |
454 uint32_t hash; | |
455 ngx_stream_upstream_hash_srv_conf_t *hcf; | |
456 ngx_stream_upstream_hash_peer_data_t *hp; | |
457 | |
458 if (ngx_stream_upstream_init_hash_peer(s, us) != NGX_OK) { | |
459 return NGX_ERROR; | |
460 } | |
461 | |
462 s->upstream->peer.get = ngx_stream_upstream_get_chash_peer; | |
463 | |
464 hp = s->upstream->peer.data; | |
465 hcf = ngx_stream_conf_upstream_srv_conf(us, | |
466 ngx_stream_upstream_hash_module); | |
467 | |
468 hash = ngx_crc32_long(hp->key.data, hp->key.len); | |
469 | |
470 ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers); | |
471 | |
472 hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash); | |
473 | |
474 ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); | |
475 | |
476 return NGX_OK; | |
477 } | |
478 | |
479 | |
480 static ngx_int_t | |
481 ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) | |
482 { | |
483 ngx_stream_upstream_hash_peer_data_t *hp = data; | |
484 | |
485 time_t now; | |
486 intptr_t m; | |
487 ngx_str_t *server; | |
488 ngx_int_t total; | |
489 ngx_uint_t i, n, best_i; | |
490 ngx_stream_upstream_rr_peer_t *peer, *best; | |
491 ngx_stream_upstream_chash_point_t *point; | |
492 ngx_stream_upstream_chash_points_t *points; | |
493 ngx_stream_upstream_hash_srv_conf_t *hcf; | |
494 | |
495 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
496 "get consistent hash peer, try: %ui", pc->tries); | |
497 | |
498 ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers); | |
499 | |
500 pc->connection = NULL; | |
501 | |
502 now = ngx_time(); | |
503 hcf = hp->conf; | |
504 | |
505 points = hcf->points; | |
506 point = &points->point[0]; | |
507 | |
508 for ( ;; ) { | |
509 server = point[hp->hash % points->number].server; | |
510 | |
511 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, | |
512 "consistent hash peer:%uD, server:\"%V\"", | |
513 hp->hash, server); | |
514 | |
515 best = NULL; | |
516 best_i = 0; | |
517 total = 0; | |
518 | |
519 for (peer = hp->rrp.peers->peer, i = 0; | |
520 peer; | |
521 peer = peer->next, i++) | |
522 { | |
523 | |
524 n = i / (8 * sizeof(uintptr_t)); | |
525 m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); | |
526 | |
527 if (hp->rrp.tried[n] & m) { | |
528 continue; | |
529 } | |
530 | |
531 if (peer->down) { | |
532 continue; | |
533 } | |
534 | |
535 if (peer->server.len != server->len | |
536 || ngx_strncmp(peer->server.data, server->data, server->len) | |
537 != 0) | |
538 { | |
539 continue; | |
540 } | |
541 | |
542 if (peer->max_fails | |
543 && peer->fails >= peer->max_fails | |
544 && now - peer->checked <= peer->fail_timeout) | |
545 { | |
546 continue; | |
547 } | |
548 | |
549 peer->current_weight += peer->effective_weight; | |
550 total += peer->effective_weight; | |
551 | |
552 if (peer->effective_weight < peer->weight) { | |
553 peer->effective_weight++; | |
554 } | |
555 | |
556 if (best == NULL || peer->current_weight > best->current_weight) { | |
557 best = peer; | |
558 best_i = i; | |
559 } | |
560 } | |
561 | |
562 if (best) { | |
563 best->current_weight -= total; | |
564 break; | |
565 } | |
566 | |
567 hp->hash++; | |
568 hp->tries++; | |
569 | |
570 if (hp->tries >= points->number) { | |
571 ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); | |
572 return NGX_BUSY; | |
573 } | |
574 } | |
575 | |
576 hp->rrp.current = best; | |
577 | |
578 pc->sockaddr = best->sockaddr; | |
579 pc->socklen = best->socklen; | |
580 pc->name = &best->name; | |
581 | |
582 best->conns++; | |
583 | |
584 if (now - best->checked > best->fail_timeout) { | |
585 best->checked = now; | |
586 } | |
587 | |
588 ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); | |
589 | |
590 n = best_i / (8 * sizeof(uintptr_t)); | |
591 m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t)); | |
592 | |
593 hp->rrp.tried[n] |= m; | |
594 | |
595 return NGX_OK; | |
596 } | |
597 | |
598 | |
599 static void * | |
600 ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf) | |
601 { | |
602 ngx_stream_upstream_hash_srv_conf_t *conf; | |
603 | |
604 conf = ngx_palloc(cf->pool, sizeof(ngx_stream_upstream_hash_srv_conf_t)); | |
605 if (conf == NULL) { | |
606 return NULL; | |
607 } | |
608 | |
609 conf->points = NULL; | |
610 | |
611 return conf; | |
612 } | |
613 | |
614 | |
615 static char * | |
616 ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
617 { | |
618 ngx_str_t *value; | |
619 ngx_stream_upstream_srv_conf_t *uscf; | |
620 | |
621 value = cf->args->elts; | |
622 | |
623 if (ngx_strcmp(value[1].data, "$remote_addr")) { | |
624 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
625 "unsupported hash key \"%V\", use $remote_addr", | |
626 &value[1]); | |
627 return NGX_CONF_ERROR; | |
628 } | |
629 | |
630 uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); | |
631 | |
632 if (uscf->peer.init_upstream) { | |
633 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
634 "load balancing method redefined"); | |
635 } | |
636 | |
637 uscf->flags = NGX_STREAM_UPSTREAM_CREATE | |
638 |NGX_STREAM_UPSTREAM_WEIGHT | |
639 |NGX_STREAM_UPSTREAM_MAX_FAILS | |
640 |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT | |
641 |NGX_STREAM_UPSTREAM_DOWN; | |
642 | |
643 if (cf->args->nelts == 2) { | |
644 uscf->peer.init_upstream = ngx_stream_upstream_init_hash; | |
645 | |
646 } else if (ngx_strcmp(value[2].data, "consistent") == 0) { | |
647 uscf->peer.init_upstream = ngx_stream_upstream_init_chash; | |
648 | |
649 } else { | |
650 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
651 "invalid parameter \"%V\"", &value[2]); | |
652 return NGX_CONF_ERROR; | |
653 } | |
654 | |
655 return NGX_CONF_OK; | |
656 } |