Mercurial > hg > nginx
annotate src/stream/ngx_stream_core_module.c @ 6153:4f6efabcb09b
The "reuseport" option of the "listen" directive.
When configured, an individual listen socket on a given address is
created for each worker process. This allows to reduce in-kernel lock
contention on configurations with high accept rates, resulting in better
performance. As of now it works on Linux and DragonFly BSD.
Note that on Linux incoming connection requests are currently tied up
to a specific listen socket, and if some sockets are closed, connection
requests will be reset, see https://lwn.net/Articles/542629/. With
nginx, this may happen if the number of worker processes is reduced.
There is no such problem on DragonFly BSD.
Based on previous work by Sepherosa Ziehau and Yingqi Lu.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Wed, 20 May 2015 15:51:56 +0300 |
parents | 61d7ae76647d |
children | c13091e6292c |
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 static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf); | |
14 static void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf); | |
15 static char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, | |
16 void *child); | |
17 static char *ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, | |
18 void *conf); | |
19 static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, | |
20 void *conf); | |
21 static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, | |
22 void *conf); | |
23 | |
24 | |
25 static ngx_command_t ngx_stream_core_commands[] = { | |
26 | |
27 { ngx_string("server"), | |
28 NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, | |
29 ngx_stream_core_server, | |
30 0, | |
31 0, | |
32 NULL }, | |
33 | |
34 { ngx_string("listen"), | |
35 NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, | |
36 ngx_stream_core_listen, | |
37 NGX_STREAM_SRV_CONF_OFFSET, | |
38 0, | |
39 NULL }, | |
40 | |
41 { ngx_string("error_log"), | |
42 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, | |
43 ngx_stream_core_error_log, | |
44 NGX_STREAM_SRV_CONF_OFFSET, | |
45 0, | |
46 NULL }, | |
47 | |
48 ngx_null_command | |
49 }; | |
50 | |
51 | |
52 static ngx_stream_module_t ngx_stream_core_module_ctx = { | |
53 ngx_stream_core_create_main_conf, /* create main configuration */ | |
54 NULL, /* init main configuration */ | |
55 | |
56 ngx_stream_core_create_srv_conf, /* create server configuration */ | |
57 ngx_stream_core_merge_srv_conf /* merge server configuration */ | |
58 }; | |
59 | |
60 | |
61 ngx_module_t ngx_stream_core_module = { | |
62 NGX_MODULE_V1, | |
63 &ngx_stream_core_module_ctx, /* module context */ | |
64 ngx_stream_core_commands, /* module directives */ | |
65 NGX_STREAM_MODULE, /* module type */ | |
66 NULL, /* init master */ | |
67 NULL, /* init module */ | |
68 NULL, /* init process */ | |
69 NULL, /* init thread */ | |
70 NULL, /* exit thread */ | |
71 NULL, /* exit process */ | |
72 NULL, /* exit master */ | |
73 NGX_MODULE_V1_PADDING | |
74 }; | |
75 | |
76 | |
77 static void * | |
78 ngx_stream_core_create_main_conf(ngx_conf_t *cf) | |
79 { | |
80 ngx_stream_core_main_conf_t *cmcf; | |
81 | |
82 cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_main_conf_t)); | |
83 if (cmcf == NULL) { | |
84 return NULL; | |
85 } | |
86 | |
87 if (ngx_array_init(&cmcf->servers, cf->pool, 4, | |
88 sizeof(ngx_stream_core_srv_conf_t *)) | |
89 != NGX_OK) | |
90 { | |
91 return NULL; | |
92 } | |
93 | |
94 if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t)) | |
95 != NGX_OK) | |
96 { | |
97 return NULL; | |
98 } | |
99 | |
100 return cmcf; | |
101 } | |
102 | |
103 | |
104 static void * | |
105 ngx_stream_core_create_srv_conf(ngx_conf_t *cf) | |
106 { | |
107 ngx_stream_core_srv_conf_t *cscf; | |
108 | |
109 cscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_srv_conf_t)); | |
110 if (cscf == NULL) { | |
111 return NULL; | |
112 } | |
113 | |
114 /* | |
115 * set by ngx_pcalloc(): | |
116 * | |
117 * cscf->handler = NULL; | |
118 * cscf->error_log = NULL; | |
119 */ | |
120 | |
121 cscf->file_name = cf->conf_file->file.name.data; | |
122 cscf->line = cf->conf_file->line; | |
123 | |
124 return cscf; | |
125 } | |
126 | |
127 | |
128 static char * | |
129 ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) | |
130 { | |
131 ngx_stream_core_srv_conf_t *prev = parent; | |
132 ngx_stream_core_srv_conf_t *conf = child; | |
133 | |
134 if (conf->handler == NULL) { | |
135 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
136 "no handler for server in %s:%ui", | |
137 conf->file_name, conf->line); | |
138 return NGX_CONF_ERROR; | |
139 } | |
140 | |
141 if (conf->error_log == NULL) { | |
142 if (prev->error_log) { | |
143 conf->error_log = prev->error_log; | |
144 } else { | |
145 conf->error_log = &cf->cycle->new_log; | |
146 } | |
147 } | |
148 | |
149 return NGX_CONF_OK; | |
150 } | |
151 | |
152 | |
153 static char * | |
154 ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
155 { | |
156 ngx_stream_core_srv_conf_t *cscf = conf; | |
157 | |
158 return ngx_log_set_log(cf, &cscf->error_log); | |
159 } | |
160 | |
161 | |
162 static char * | |
163 ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
164 { | |
165 char *rv; | |
166 void *mconf; | |
167 ngx_uint_t m; | |
168 ngx_conf_t pcf; | |
169 ngx_stream_module_t *module; | |
170 ngx_stream_conf_ctx_t *ctx, *stream_ctx; | |
171 ngx_stream_core_srv_conf_t *cscf, **cscfp; | |
172 ngx_stream_core_main_conf_t *cmcf; | |
173 | |
174 ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t)); | |
175 if (ctx == NULL) { | |
176 return NGX_CONF_ERROR; | |
177 } | |
178 | |
179 stream_ctx = cf->ctx; | |
180 ctx->main_conf = stream_ctx->main_conf; | |
181 | |
182 /* the server{}'s srv_conf */ | |
183 | |
184 ctx->srv_conf = ngx_pcalloc(cf->pool, | |
185 sizeof(void *) * ngx_stream_max_module); | |
186 if (ctx->srv_conf == NULL) { | |
187 return NGX_CONF_ERROR; | |
188 } | |
189 | |
190 for (m = 0; ngx_modules[m]; m++) { | |
191 if (ngx_modules[m]->type != NGX_STREAM_MODULE) { | |
192 continue; | |
193 } | |
194 | |
195 module = ngx_modules[m]->ctx; | |
196 | |
197 if (module->create_srv_conf) { | |
198 mconf = module->create_srv_conf(cf); | |
199 if (mconf == NULL) { | |
200 return NGX_CONF_ERROR; | |
201 } | |
202 | |
203 ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf; | |
204 } | |
205 } | |
206 | |
207 /* the server configuration context */ | |
208 | |
209 cscf = ctx->srv_conf[ngx_stream_core_module.ctx_index]; | |
210 cscf->ctx = ctx; | |
211 | |
212 cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index]; | |
213 | |
214 cscfp = ngx_array_push(&cmcf->servers); | |
215 if (cscfp == NULL) { | |
216 return NGX_CONF_ERROR; | |
217 } | |
218 | |
219 *cscfp = cscf; | |
220 | |
221 | |
222 /* parse inside server{} */ | |
223 | |
224 pcf = *cf; | |
225 cf->ctx = ctx; | |
226 cf->cmd_type = NGX_STREAM_SRV_CONF; | |
227 | |
228 rv = ngx_conf_parse(cf, NULL); | |
229 | |
230 *cf = pcf; | |
231 | |
232 return rv; | |
233 } | |
234 | |
235 | |
236 static char * | |
237 ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
238 { | |
239 size_t len, off; | |
240 in_port_t port; | |
241 ngx_str_t *value; | |
242 ngx_url_t u; | |
243 ngx_uint_t i; | |
244 struct sockaddr *sa; | |
245 struct sockaddr_in *sin; | |
246 ngx_stream_listen_t *ls; | |
247 ngx_stream_core_main_conf_t *cmcf; | |
248 #if (NGX_HAVE_INET6) | |
249 struct sockaddr_in6 *sin6; | |
250 #endif | |
251 | |
252 value = cf->args->elts; | |
253 | |
254 ngx_memzero(&u, sizeof(ngx_url_t)); | |
255 | |
256 u.url = value[1]; | |
257 u.listen = 1; | |
258 | |
259 if (ngx_parse_url(cf->pool, &u) != NGX_OK) { | |
260 if (u.err) { | |
261 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
262 "%s in \"%V\" of the \"listen\" directive", | |
263 u.err, &u.url); | |
264 } | |
265 | |
266 return NGX_CONF_ERROR; | |
267 } | |
268 | |
269 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); | |
270 | |
271 ls = cmcf->listen.elts; | |
272 | |
273 for (i = 0; i < cmcf->listen.nelts; i++) { | |
274 | |
275 sa = (struct sockaddr *) ls[i].sockaddr; | |
276 | |
277 if (sa->sa_family != u.family) { | |
278 continue; | |
279 } | |
280 | |
281 switch (sa->sa_family) { | |
282 | |
283 #if (NGX_HAVE_INET6) | |
284 case AF_INET6: | |
285 off = offsetof(struct sockaddr_in6, sin6_addr); | |
286 len = 16; | |
287 sin6 = (struct sockaddr_in6 *) sa; | |
288 port = sin6->sin6_port; | |
289 break; | |
290 #endif | |
291 | |
292 #if (NGX_HAVE_UNIX_DOMAIN) | |
293 case AF_UNIX: | |
294 off = offsetof(struct sockaddr_un, sun_path); | |
295 len = sizeof(((struct sockaddr_un *) sa)->sun_path); | |
296 port = 0; | |
297 break; | |
298 #endif | |
299 | |
300 default: /* AF_INET */ | |
301 off = offsetof(struct sockaddr_in, sin_addr); | |
302 len = 4; | |
303 sin = (struct sockaddr_in *) sa; | |
304 port = sin->sin_port; | |
305 break; | |
306 } | |
307 | |
308 if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) { | |
309 continue; | |
310 } | |
311 | |
312 if (port != u.port) { | |
313 continue; | |
314 } | |
315 | |
316 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
317 "duplicate \"%V\" address and port pair", &u.url); | |
318 return NGX_CONF_ERROR; | |
319 } | |
320 | |
321 ls = ngx_array_push(&cmcf->listen); | |
322 if (ls == NULL) { | |
323 return NGX_CONF_ERROR; | |
324 } | |
325 | |
326 ngx_memzero(ls, sizeof(ngx_stream_listen_t)); | |
327 | |
328 ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen); | |
329 | |
330 ls->socklen = u.socklen; | |
331 ls->wildcard = u.wildcard; | |
332 ls->ctx = cf->ctx; | |
333 | |
334 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) | |
335 ls->ipv6only = 1; | |
336 #endif | |
337 | |
338 for (i = 2; i < cf->args->nelts; i++) { | |
339 | |
340 if (ngx_strcmp(value[i].data, "bind") == 0) { | |
341 ls->bind = 1; | |
342 continue; | |
343 } | |
344 | |
345 if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { | |
346 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) | |
347 struct sockaddr *sa; | |
348 u_char buf[NGX_SOCKADDR_STRLEN]; | |
349 | |
350 sa = (struct sockaddr *) ls->sockaddr; | |
351 | |
352 if (sa->sa_family == AF_INET6) { | |
353 | |
354 if (ngx_strcmp(&value[i].data[10], "n") == 0) { | |
355 ls->ipv6only = 1; | |
356 | |
357 } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) { | |
358 ls->ipv6only = 0; | |
359 | |
360 } else { | |
361 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
362 "invalid ipv6only flags \"%s\"", | |
363 &value[i].data[9]); | |
364 return NGX_CONF_ERROR; | |
365 } | |
366 | |
367 ls->bind = 1; | |
368 | |
369 } else { | |
370 len = ngx_sock_ntop(sa, ls->socklen, buf, | |
371 NGX_SOCKADDR_STRLEN, 1); | |
372 | |
373 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
374 "ipv6only is not supported " | |
375 "on addr \"%*s\", ignored", len, buf); | |
376 } | |
377 | |
378 continue; | |
379 #else | |
380 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
381 "bind ipv6only is not supported " | |
382 "on this platform"); | |
383 return NGX_CONF_ERROR; | |
384 #endif | |
385 } | |
386 | |
6153
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
387 if (ngx_strcmp(value[i].data, "reuseport") == 0) { |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
388 #if (NGX_HAVE_REUSEPORT) |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
389 ls->reuseport = 1; |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
390 ls->bind = 1; |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
391 #else |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
392 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
393 "reuseport is not supported " |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
394 "on this platform, ignored"); |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
395 #endif |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
396 continue; |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
397 } |
4f6efabcb09b
The "reuseport" option of the "listen" directive.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6115
diff
changeset
|
398 |
6115 | 399 if (ngx_strcmp(value[i].data, "ssl") == 0) { |
400 #if (NGX_STREAM_SSL) | |
401 ls->ssl = 1; | |
402 continue; | |
403 #else | |
404 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
405 "the \"ssl\" parameter requires " | |
406 "ngx_stream_ssl_module"); | |
407 return NGX_CONF_ERROR; | |
408 #endif | |
409 } | |
410 | |
411 if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) { | |
412 | |
413 if (ngx_strcmp(&value[i].data[13], "on") == 0) { | |
414 ls->so_keepalive = 1; | |
415 | |
416 } else if (ngx_strcmp(&value[i].data[13], "off") == 0) { | |
417 ls->so_keepalive = 2; | |
418 | |
419 } else { | |
420 | |
421 #if (NGX_HAVE_KEEPALIVE_TUNABLE) | |
422 u_char *p, *end; | |
423 ngx_str_t s; | |
424 | |
425 end = value[i].data + value[i].len; | |
426 s.data = value[i].data + 13; | |
427 | |
428 p = ngx_strlchr(s.data, end, ':'); | |
429 if (p == NULL) { | |
430 p = end; | |
431 } | |
432 | |
433 if (p > s.data) { | |
434 s.len = p - s.data; | |
435 | |
436 ls->tcp_keepidle = ngx_parse_time(&s, 1); | |
437 if (ls->tcp_keepidle == (time_t) NGX_ERROR) { | |
438 goto invalid_so_keepalive; | |
439 } | |
440 } | |
441 | |
442 s.data = (p < end) ? (p + 1) : end; | |
443 | |
444 p = ngx_strlchr(s.data, end, ':'); | |
445 if (p == NULL) { | |
446 p = end; | |
447 } | |
448 | |
449 if (p > s.data) { | |
450 s.len = p - s.data; | |
451 | |
452 ls->tcp_keepintvl = ngx_parse_time(&s, 1); | |
453 if (ls->tcp_keepintvl == (time_t) NGX_ERROR) { | |
454 goto invalid_so_keepalive; | |
455 } | |
456 } | |
457 | |
458 s.data = (p < end) ? (p + 1) : end; | |
459 | |
460 if (s.data < end) { | |
461 s.len = end - s.data; | |
462 | |
463 ls->tcp_keepcnt = ngx_atoi(s.data, s.len); | |
464 if (ls->tcp_keepcnt == NGX_ERROR) { | |
465 goto invalid_so_keepalive; | |
466 } | |
467 } | |
468 | |
469 if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0 | |
470 && ls->tcp_keepcnt == 0) | |
471 { | |
472 goto invalid_so_keepalive; | |
473 } | |
474 | |
475 ls->so_keepalive = 1; | |
476 | |
477 #else | |
478 | |
479 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
480 "the \"so_keepalive\" parameter accepts " | |
481 "only \"on\" or \"off\" on this platform"); | |
482 return NGX_CONF_ERROR; | |
483 | |
484 #endif | |
485 } | |
486 | |
487 ls->bind = 1; | |
488 | |
489 continue; | |
490 | |
491 #if (NGX_HAVE_KEEPALIVE_TUNABLE) | |
492 invalid_so_keepalive: | |
493 | |
494 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
495 "invalid so_keepalive value: \"%s\"", | |
496 &value[i].data[13]); | |
497 return NGX_CONF_ERROR; | |
498 #endif | |
499 } | |
500 | |
501 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
502 "the invalid \"%V\" parameter", &value[i]); | |
503 return NGX_CONF_ERROR; | |
504 } | |
505 | |
506 return NGX_CONF_OK; | |
507 } |