Mercurial > hg > nginx-quic
annotate src/http/modules/ngx_http_upstream_keepalive_module.c @ 5885:5a042519bfe7
Upstream: limited next_upstream time and tries when resolving DNS.
When got multiple upstream IP addresses using DNS resolving, the number of
upstreams tries and the maxinum time spent for these tries were not affected.
This patch fixed it.
author | Gu Feng <flygoast@126.com> |
---|---|
date | Mon, 27 Oct 2014 19:52:03 +0800 |
parents | 39befd3c0d84 |
children | 4d8936b1fc32 |
rev | line source |
---|---|
4127 | 1 |
2 /* | |
3 * Copyright (C) Maxim Dounin | |
4412 | 4 * Copyright (C) Nginx, Inc. |
4127 | 5 */ |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 typedef struct { | |
14 ngx_uint_t max_cached; | |
15 | |
16 ngx_queue_t cache; | |
17 ngx_queue_t free; | |
18 | |
19 ngx_http_upstream_init_pt original_init_upstream; | |
20 ngx_http_upstream_init_peer_pt original_init_peer; | |
21 | |
22 } ngx_http_upstream_keepalive_srv_conf_t; | |
23 | |
24 | |
25 typedef struct { | |
26 ngx_http_upstream_keepalive_srv_conf_t *conf; | |
27 | |
28 ngx_http_upstream_t *upstream; | |
29 | |
30 void *data; | |
31 | |
32 ngx_event_get_peer_pt original_get_peer; | |
33 ngx_event_free_peer_pt original_free_peer; | |
34 | |
35 #if (NGX_HTTP_SSL) | |
36 ngx_event_set_peer_session_pt original_set_session; | |
37 ngx_event_save_peer_session_pt original_save_session; | |
38 #endif | |
39 | |
40 } ngx_http_upstream_keepalive_peer_data_t; | |
41 | |
42 | |
43 typedef struct { | |
44 ngx_http_upstream_keepalive_srv_conf_t *conf; | |
45 | |
46 ngx_queue_t queue; | |
47 ngx_connection_t *connection; | |
48 | |
49 socklen_t socklen; | |
50 u_char sockaddr[NGX_SOCKADDRLEN]; | |
51 | |
52 } ngx_http_upstream_keepalive_cache_t; | |
53 | |
54 | |
55 static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r, | |
56 ngx_http_upstream_srv_conf_t *us); | |
57 static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, | |
58 void *data); | |
59 static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, | |
60 void *data, ngx_uint_t state); | |
61 | |
62 static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev); | |
63 static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev); | |
64 static void ngx_http_upstream_keepalive_close(ngx_connection_t *c); | |
65 | |
66 | |
67 #if (NGX_HTTP_SSL) | |
68 static ngx_int_t ngx_http_upstream_keepalive_set_session( | |
69 ngx_peer_connection_t *pc, void *data); | |
70 static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, | |
71 void *data); | |
72 #endif | |
73 | |
74 static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf); | |
75 static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, | |
76 void *conf); | |
77 | |
78 | |
79 static ngx_command_t ngx_http_upstream_keepalive_commands[] = { | |
80 | |
81 { ngx_string("keepalive"), | |
5830
3e5b630e0ec9
Upstream keepalive: removed "single" parameter remnants.
Maxim Dounin <mdounin@mdounin.ru>
parents:
5213
diff
changeset
|
82 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, |
4127 | 83 ngx_http_upstream_keepalive, |
5213
822b82191940
Upstream keepalive: slightly simplified code.
Ruslan Ermilov <ru@nginx.com>
parents:
5133
diff
changeset
|
84 NGX_HTTP_SRV_CONF_OFFSET, |
4127 | 85 0, |
86 NULL }, | |
87 | |
88 ngx_null_command | |
89 }; | |
90 | |
91 | |
92 static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = { | |
93 NULL, /* preconfiguration */ | |
94 NULL, /* postconfiguration */ | |
95 | |
96 NULL, /* create main configuration */ | |
97 NULL, /* init main configuration */ | |
98 | |
99 ngx_http_upstream_keepalive_create_conf, /* create server configuration */ | |
100 NULL, /* merge server configuration */ | |
101 | |
102 NULL, /* create location configuration */ | |
103 NULL /* merge location configuration */ | |
104 }; | |
105 | |
106 | |
107 ngx_module_t ngx_http_upstream_keepalive_module = { | |
108 NGX_MODULE_V1, | |
109 &ngx_http_upstream_keepalive_module_ctx, /* module context */ | |
110 ngx_http_upstream_keepalive_commands, /* module directives */ | |
111 NGX_HTTP_MODULE, /* module type */ | |
112 NULL, /* init master */ | |
113 NULL, /* init module */ | |
114 NULL, /* init process */ | |
115 NULL, /* init thread */ | |
116 NULL, /* exit thread */ | |
117 NULL, /* exit process */ | |
118 NULL, /* exit master */ | |
119 NGX_MODULE_V1_PADDING | |
120 }; | |
121 | |
122 | |
123 static ngx_int_t | |
124 ngx_http_upstream_init_keepalive(ngx_conf_t *cf, | |
125 ngx_http_upstream_srv_conf_t *us) | |
126 { | |
127 ngx_uint_t i; | |
128 ngx_http_upstream_keepalive_srv_conf_t *kcf; | |
129 ngx_http_upstream_keepalive_cache_t *cached; | |
130 | |
131 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, | |
132 "init keepalive"); | |
133 | |
134 kcf = ngx_http_conf_upstream_srv_conf(us, | |
135 ngx_http_upstream_keepalive_module); | |
136 | |
137 if (kcf->original_init_upstream(cf, us) != NGX_OK) { | |
138 return NGX_ERROR; | |
139 } | |
140 | |
141 kcf->original_init_peer = us->peer.init; | |
142 | |
143 us->peer.init = ngx_http_upstream_init_keepalive_peer; | |
144 | |
145 /* allocate cache items and add to free queue */ | |
146 | |
147 cached = ngx_pcalloc(cf->pool, | |
148 sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached); | |
149 if (cached == NULL) { | |
150 return NGX_ERROR; | |
151 } | |
152 | |
153 ngx_queue_init(&kcf->cache); | |
154 ngx_queue_init(&kcf->free); | |
155 | |
156 for (i = 0; i < kcf->max_cached; i++) { | |
157 ngx_queue_insert_head(&kcf->free, &cached[i].queue); | |
158 cached[i].conf = kcf; | |
159 } | |
160 | |
161 return NGX_OK; | |
162 } | |
163 | |
164 | |
165 static ngx_int_t | |
166 ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r, | |
167 ngx_http_upstream_srv_conf_t *us) | |
168 { | |
169 ngx_http_upstream_keepalive_peer_data_t *kp; | |
170 ngx_http_upstream_keepalive_srv_conf_t *kcf; | |
171 | |
172 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
173 "init keepalive peer"); | |
174 | |
175 kcf = ngx_http_conf_upstream_srv_conf(us, | |
176 ngx_http_upstream_keepalive_module); | |
177 | |
178 kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t)); | |
179 if (kp == NULL) { | |
180 return NGX_ERROR; | |
181 } | |
182 | |
183 if (kcf->original_init_peer(r, us) != NGX_OK) { | |
184 return NGX_ERROR; | |
185 } | |
186 | |
187 kp->conf = kcf; | |
188 kp->upstream = r->upstream; | |
189 kp->data = r->upstream->peer.data; | |
190 kp->original_get_peer = r->upstream->peer.get; | |
191 kp->original_free_peer = r->upstream->peer.free; | |
192 | |
193 r->upstream->peer.data = kp; | |
194 r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer; | |
195 r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer; | |
196 | |
197 #if (NGX_HTTP_SSL) | |
198 kp->original_set_session = r->upstream->peer.set_session; | |
199 kp->original_save_session = r->upstream->peer.save_session; | |
200 r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session; | |
201 r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session; | |
202 #endif | |
203 | |
204 return NGX_OK; | |
205 } | |
206 | |
207 | |
208 static ngx_int_t | |
209 ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data) | |
210 { | |
211 ngx_http_upstream_keepalive_peer_data_t *kp = data; | |
212 ngx_http_upstream_keepalive_cache_t *item; | |
213 | |
214 ngx_int_t rc; | |
215 ngx_queue_t *q, *cache; | |
216 ngx_connection_t *c; | |
217 | |
218 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
219 "get keepalive peer"); | |
220 | |
4694
5b5c07dee156
Upstream keepalive: "single" parameter deprecated.
Maxim Dounin <mdounin@mdounin.ru>
parents:
4412
diff
changeset
|
221 /* ask balancer */ |
4127 | 222 |
223 rc = kp->original_get_peer(pc, kp->data); | |
224 | |
4694
5b5c07dee156
Upstream keepalive: "single" parameter deprecated.
Maxim Dounin <mdounin@mdounin.ru>
parents:
4412
diff
changeset
|
225 if (rc != NGX_OK) { |
4127 | 226 return rc; |
227 } | |
228 | |
229 /* search cache for suitable connection */ | |
230 | |
231 cache = &kp->conf->cache; | |
232 | |
233 for (q = ngx_queue_head(cache); | |
234 q != ngx_queue_sentinel(cache); | |
235 q = ngx_queue_next(q)) | |
236 { | |
237 item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); | |
238 c = item->connection; | |
239 | |
240 if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr, | |
241 item->socklen, pc->socklen) | |
242 == 0) | |
243 { | |
244 ngx_queue_remove(q); | |
245 ngx_queue_insert_head(&kp->conf->free, q); | |
246 | |
247 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
248 "get keepalive peer: using connection %p", c); | |
249 | |
250 c->idle = 0; | |
5864
39befd3c0d84
Upstream keepalive: reset c->sent on cached connections.
Maxim Dounin <mdounin@mdounin.ru>
parents:
5830
diff
changeset
|
251 c->sent = 0; |
4127 | 252 c->log = pc->log; |
253 c->read->log = pc->log; | |
254 c->write->log = pc->log; | |
255 c->pool->log = pc->log; | |
256 | |
257 pc->connection = c; | |
258 pc->cached = 1; | |
259 | |
260 return NGX_DONE; | |
261 } | |
262 } | |
263 | |
264 return NGX_OK; | |
265 } | |
266 | |
267 | |
268 static void | |
269 ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data, | |
270 ngx_uint_t state) | |
271 { | |
272 ngx_http_upstream_keepalive_peer_data_t *kp = data; | |
273 ngx_http_upstream_keepalive_cache_t *item; | |
274 | |
275 ngx_queue_t *q; | |
276 ngx_connection_t *c; | |
277 ngx_http_upstream_t *u; | |
278 | |
279 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
280 "free keepalive peer"); | |
281 | |
282 /* cache valid connections */ | |
283 | |
284 u = kp->upstream; | |
285 c = pc->connection; | |
286 | |
5133
089a662c17d1
Upstream: removed double-free workarounds in peer.free() methods.
Ruslan Ermilov <ru@nginx.com>
parents:
4998
diff
changeset
|
287 if (state & NGX_PEER_FAILED |
4127 | 288 || c == NULL |
289 || c->read->eof | |
290 || c->read->error | |
291 || c->read->timedout | |
292 || c->write->error | |
293 || c->write->timedout) | |
294 { | |
295 goto invalid; | |
296 } | |
297 | |
298 if (!u->keepalive) { | |
299 goto invalid; | |
300 } | |
301 | |
302 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
303 goto invalid; | |
304 } | |
305 | |
306 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
307 "free keepalive peer: saving connection %p", c); | |
308 | |
309 if (ngx_queue_empty(&kp->conf->free)) { | |
310 | |
311 q = ngx_queue_last(&kp->conf->cache); | |
312 ngx_queue_remove(q); | |
313 | |
314 item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); | |
315 | |
316 ngx_http_upstream_keepalive_close(item->connection); | |
317 | |
318 } else { | |
319 q = ngx_queue_head(&kp->conf->free); | |
320 ngx_queue_remove(q); | |
321 | |
322 item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); | |
323 } | |
324 | |
325 item->connection = c; | |
326 ngx_queue_insert_head(&kp->conf->cache, q); | |
327 | |
328 pc->connection = NULL; | |
329 | |
330 if (c->read->timer_set) { | |
331 ngx_del_timer(c->read); | |
332 } | |
333 if (c->write->timer_set) { | |
334 ngx_del_timer(c->write); | |
335 } | |
336 | |
337 c->write->handler = ngx_http_upstream_keepalive_dummy_handler; | |
338 c->read->handler = ngx_http_upstream_keepalive_close_handler; | |
339 | |
340 c->data = item; | |
341 c->idle = 1; | |
342 c->log = ngx_cycle->log; | |
343 c->read->log = ngx_cycle->log; | |
344 c->write->log = ngx_cycle->log; | |
345 c->pool->log = ngx_cycle->log; | |
346 | |
347 item->socklen = pc->socklen; | |
348 ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); | |
349 | |
350 if (c->read->ready) { | |
351 ngx_http_upstream_keepalive_close_handler(c->read); | |
352 } | |
353 | |
354 invalid: | |
355 | |
356 kp->original_free_peer(pc, kp->data, state); | |
357 } | |
358 | |
359 | |
360 static void | |
361 ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev) | |
362 { | |
363 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, | |
364 "keepalive dummy handler"); | |
365 } | |
366 | |
367 | |
368 static void | |
369 ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev) | |
370 { | |
371 ngx_http_upstream_keepalive_srv_conf_t *conf; | |
372 ngx_http_upstream_keepalive_cache_t *item; | |
373 | |
374 int n; | |
375 char buf[1]; | |
376 ngx_connection_t *c; | |
377 | |
378 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, | |
379 "keepalive close handler"); | |
380 | |
381 c = ev->data; | |
382 | |
383 if (c->close) { | |
384 goto close; | |
385 } | |
386 | |
387 n = recv(c->fd, buf, 1, MSG_PEEK); | |
388 | |
389 if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { | |
390 /* stale event */ | |
391 | |
392 if (ngx_handle_read_event(c->read, 0) != NGX_OK) { | |
393 goto close; | |
394 } | |
395 | |
396 return; | |
397 } | |
398 | |
399 close: | |
400 | |
401 item = c->data; | |
402 conf = item->conf; | |
403 | |
404 ngx_http_upstream_keepalive_close(c); | |
405 | |
406 ngx_queue_remove(&item->queue); | |
407 ngx_queue_insert_head(&conf->free, &item->queue); | |
408 } | |
409 | |
410 | |
411 static void | |
412 ngx_http_upstream_keepalive_close(ngx_connection_t *c) | |
413 { | |
414 | |
415 #if (NGX_HTTP_SSL) | |
416 | |
417 if (c->ssl) { | |
418 c->ssl->no_wait_shutdown = 1; | |
419 c->ssl->no_send_shutdown = 1; | |
420 | |
421 if (ngx_ssl_shutdown(c) == NGX_AGAIN) { | |
422 c->ssl->handler = ngx_http_upstream_keepalive_close; | |
423 return; | |
424 } | |
425 } | |
426 | |
427 #endif | |
428 | |
429 ngx_destroy_pool(c->pool); | |
430 ngx_close_connection(c); | |
431 } | |
432 | |
433 | |
434 #if (NGX_HTTP_SSL) | |
435 | |
436 static ngx_int_t | |
437 ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data) | |
438 { | |
439 ngx_http_upstream_keepalive_peer_data_t *kp = data; | |
440 | |
441 return kp->original_set_session(pc, kp->data); | |
442 } | |
443 | |
444 | |
445 static void | |
446 ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data) | |
447 { | |
448 ngx_http_upstream_keepalive_peer_data_t *kp = data; | |
449 | |
450 kp->original_save_session(pc, kp->data); | |
451 return; | |
452 } | |
453 | |
454 #endif | |
455 | |
456 | |
457 static void * | |
458 ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf) | |
459 { | |
460 ngx_http_upstream_keepalive_srv_conf_t *conf; | |
461 | |
462 conf = ngx_pcalloc(cf->pool, | |
463 sizeof(ngx_http_upstream_keepalive_srv_conf_t)); | |
464 if (conf == NULL) { | |
465 return NULL; | |
466 } | |
467 | |
468 /* | |
469 * set by ngx_pcalloc(): | |
470 * | |
471 * conf->original_init_upstream = NULL; | |
472 * conf->original_init_peer = NULL; | |
473 */ | |
474 | |
475 conf->max_cached = 1; | |
476 | |
477 return conf; | |
478 } | |
479 | |
480 | |
481 static char * | |
482 ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
483 { | |
484 ngx_http_upstream_srv_conf_t *uscf; | |
5213
822b82191940
Upstream keepalive: slightly simplified code.
Ruslan Ermilov <ru@nginx.com>
parents:
5133
diff
changeset
|
485 ngx_http_upstream_keepalive_srv_conf_t *kcf = conf; |
4127 | 486 |
487 ngx_int_t n; | |
488 ngx_str_t *value; | |
489 | |
490 uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); | |
491 | |
4998
82336a9ce26d
Upstream keepalive: detect duplicate "keepalive" directive.
Ruslan Ermilov <ru@nginx.com>
parents:
4694
diff
changeset
|
492 if (kcf->original_init_upstream) { |
82336a9ce26d
Upstream keepalive: detect duplicate "keepalive" directive.
Ruslan Ermilov <ru@nginx.com>
parents:
4694
diff
changeset
|
493 return "is duplicate"; |
82336a9ce26d
Upstream keepalive: detect duplicate "keepalive" directive.
Ruslan Ermilov <ru@nginx.com>
parents:
4694
diff
changeset
|
494 } |
82336a9ce26d
Upstream keepalive: detect duplicate "keepalive" directive.
Ruslan Ermilov <ru@nginx.com>
parents:
4694
diff
changeset
|
495 |
4127 | 496 kcf->original_init_upstream = uscf->peer.init_upstream |
497 ? uscf->peer.init_upstream | |
498 : ngx_http_upstream_init_round_robin; | |
499 | |
500 uscf->peer.init_upstream = ngx_http_upstream_init_keepalive; | |
501 | |
502 /* read options */ | |
503 | |
504 value = cf->args->elts; | |
505 | |
506 n = ngx_atoi(value[1].data, value[1].len); | |
507 | |
508 if (n == NGX_ERROR || n == 0) { | |
509 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
510 "invalid value \"%V\" in \"%V\" directive", | |
511 &value[1], &cmd->name); | |
512 return NGX_CONF_ERROR; | |
513 } | |
514 | |
515 kcf->max_cached = n; | |
516 | |
517 return NGX_CONF_OK; | |
518 } |