Mercurial > hg > nginx
comparison src/core/ngx_connection.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 | adba26ff70b5 |
children | 3096ae76ba47 |
comparison
equal
deleted
inserted
replaced
6152:3c344ea7d88b | 6153:4f6efabcb09b |
---|---|
85 #if (NGX_HAVE_TCP_FASTOPEN) | 85 #if (NGX_HAVE_TCP_FASTOPEN) |
86 ls->fastopen = -1; | 86 ls->fastopen = -1; |
87 #endif | 87 #endif |
88 | 88 |
89 return ls; | 89 return ls; |
90 } | |
91 | |
92 | |
93 ngx_int_t | |
94 ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls) | |
95 { | |
96 #if (NGX_HAVE_REUSEPORT) | |
97 | |
98 ngx_int_t n; | |
99 ngx_core_conf_t *ccf; | |
100 ngx_listening_t ols; | |
101 | |
102 if (!ls->reuseport) { | |
103 return NGX_OK; | |
104 } | |
105 | |
106 ols = *ls; | |
107 | |
108 ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, | |
109 ngx_core_module); | |
110 | |
111 for (n = 1; n < ccf->worker_processes; n++) { | |
112 | |
113 /* create a socket for each worker process */ | |
114 | |
115 ls = ngx_array_push(&cf->cycle->listening); | |
116 if (ls == NULL) { | |
117 return NGX_ERROR; | |
118 } | |
119 | |
120 *ls = ols; | |
121 ls->worker = n; | |
122 } | |
123 | |
124 #endif | |
125 | |
126 return NGX_OK; | |
90 } | 127 } |
91 | 128 |
92 | 129 |
93 ngx_int_t | 130 ngx_int_t |
94 ngx_set_inherited_sockets(ngx_cycle_t *cycle) | 131 ngx_set_inherited_sockets(ngx_cycle_t *cycle) |
104 struct accept_filter_arg af; | 141 struct accept_filter_arg af; |
105 #endif | 142 #endif |
106 #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) | 143 #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) |
107 int timeout; | 144 int timeout; |
108 #endif | 145 #endif |
146 #if (NGX_HAVE_REUSEPORT) | |
147 int reuseport; | |
148 #endif | |
109 | 149 |
110 ls = cycle->listening.elts; | 150 ls = cycle->listening.elts; |
111 for (i = 0; i < cycle->listening.nelts; i++) { | 151 for (i = 0; i < cycle->listening.nelts; i++) { |
112 | 152 |
113 ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN); | 153 ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN); |
211 | 251 |
212 ls[i].setfib = -1; | 252 ls[i].setfib = -1; |
213 } | 253 } |
214 | 254 |
215 #endif | 255 #endif |
256 #endif | |
257 | |
258 #if (NGX_HAVE_REUSEPORT) | |
259 | |
260 reuseport = 0; | |
261 olen = sizeof(int); | |
262 | |
263 if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT, | |
264 (void *) &reuseport, &olen) | |
265 == -1) | |
266 { | |
267 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, | |
268 "getsockopt(SO_REUSEPORT) %V failed, ignored", | |
269 &ls[i].addr_text); | |
270 | |
271 } else { | |
272 ls[i].reuseport = reuseport ? 1 : 0; | |
273 } | |
274 | |
216 #endif | 275 #endif |
217 | 276 |
218 #if (NGX_HAVE_TCP_FASTOPEN) | 277 #if (NGX_HAVE_TCP_FASTOPEN) |
219 | 278 |
220 olen = sizeof(int); | 279 olen = sizeof(int); |
330 | 389 |
331 if (ls[i].ignore) { | 390 if (ls[i].ignore) { |
332 continue; | 391 continue; |
333 } | 392 } |
334 | 393 |
394 #if (NGX_HAVE_REUSEPORT) | |
395 | |
396 if (ls[i].add_reuseport) { | |
397 | |
398 /* | |
399 * to allow transition from a socket without SO_REUSEPORT | |
400 * to multiple sockets with SO_REUSEPORT, we have to set | |
401 * SO_REUSEPORT on the old socket before opening new ones | |
402 */ | |
403 | |
404 int reuseport = 1; | |
405 | |
406 if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT, | |
407 (const void *) &reuseport, sizeof(int)) | |
408 == -1) | |
409 { | |
410 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, | |
411 "setsockopt(SO_REUSEPORT) %V failed, ignored", | |
412 &ls[i].addr_text); | |
413 } | |
414 | |
415 ls[i].add_reuseport = 0; | |
416 } | |
417 #endif | |
418 | |
335 if (ls[i].fd != (ngx_socket_t) -1) { | 419 if (ls[i].fd != (ngx_socket_t) -1) { |
336 continue; | 420 continue; |
337 } | 421 } |
338 | 422 |
339 if (ls[i].inherited) { | 423 if (ls[i].inherited) { |
367 &ls[i].addr_text); | 451 &ls[i].addr_text); |
368 } | 452 } |
369 | 453 |
370 return NGX_ERROR; | 454 return NGX_ERROR; |
371 } | 455 } |
456 | |
457 #if (NGX_HAVE_REUSEPORT) | |
458 | |
459 if (ls[i].reuseport) { | |
460 int reuseport; | |
461 | |
462 reuseport = 1; | |
463 | |
464 if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, | |
465 (const void *) &reuseport, sizeof(int)) | |
466 == -1) | |
467 { | |
468 ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, | |
469 "setsockopt(SO_REUSEPORT) %V failed, ignored", | |
470 &ls[i].addr_text); | |
471 | |
472 if (ngx_close_socket(s) == -1) { | |
473 ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, | |
474 ngx_close_socket_n " %V failed", | |
475 &ls[i].addr_text); | |
476 } | |
477 | |
478 return NGX_ERROR; | |
479 } | |
480 } | |
481 #endif | |
372 | 482 |
373 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) | 483 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) |
374 | 484 |
375 if (ls[i].sockaddr->sa_family == AF_INET6) { | 485 if (ls[i].sockaddr->sa_family == AF_INET6) { |
376 int ipv6only; | 486 int ipv6only; |