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