optimize: added an NGINX core patch to ensure unused listening fds are closed when 'reuseport' is used.

When `reuseport` is enabled in the `listen` directive, Nginx will create
a listening fd for each worker process in the master process.

These fds will be inherited by the worker processes, but most of them
are unused. For example, considering we have 32 listening ip:port
configurations and 64 worker processes, each worker process will inherit
2048 (32 * 64) listening fds, but only 32 fds are used. By closing the
unused fds, this change could save up to 2016 (32 * 63) fds in a worker
process.

It doesn't affect the listening socket, since there is only one used fd
which associates to the socket with or without this change.

Co-authored-by: Thibault Charbonnier <thibaultcha@me.com>
This commit is contained in:
spacewander 2019-04-20 11:35:31 +08:00 committed by Thibault Charbonnier
parent 46237a9c22
commit cf7516fcbc
3 changed files with 125 additions and 0 deletions

View File

@ -0,0 +1,38 @@
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -1118,6 +1118,12 @@ ngx_close_listening_sockets(ngx_cycle_t *cycle)
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
+#if (NGX_HAVE_REUSEPORT)
+ if (ls[i].fd == (ngx_socket_t) -1) {
+ continue;
+ }
+#endif
+
c = ls[i].connection;
if (c) {
diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -775,6 +775,18 @@ ngx_event_process_init(ngx_cycle_t *cycle)
#if (NGX_HAVE_REUSEPORT)
if (ls[i].reuseport && ls[i].worker != ngx_worker) {
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "closing unused fd:%d listening on %V",
+ ls[i].fd, &ls[i].addr_text);
+
+ if (ngx_close_socket(ls[i].fd) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ ls[i].fd = (ngx_socket_t) -1;
+
continue;
}
#endif

View File

@ -0,0 +1,83 @@
# vim:set ft= ts=4 sw=4 et fdm=marker:
use Test::Nginx::Socket::Lua;
master_on();
workers(2);
log_level('debug');
repeat_each(1);
plan tests => repeat_each() * (blocks() * 3);
no_long_string();
run_tests();
__DATA__
=== TEST 1: closes unused file descriptors when 'reuseport' is enabled
--- http_config
server {
listen 127.0.0.1:12345 reuseport;
}
--- config
location /t {
return 200;
}
--- request
GET /t
--- ignore_response_body
--- no_error_log
[error]
--- grep_error_log eval: qr/\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.1:12345/
--- grep_error_log_out eval
qr/\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.1:12345
\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.1:12345
/
=== TEST 2: closes multiple unused file descriptors when 'reuseport' is enabled
--- http_config
server {
listen 127.0.0.1:12345 reuseport;
}
server {
listen 127.0.0.2:12345 reuseport;
}
--- config
location /t {
return 200;
}
--- request
GET /t
--- ignore_response_body
--- no_error_log
[error]
--- grep_error_log eval: qr/\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.\d+:12345/
--- grep_error_log_out eval
qr/\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.1:12345
\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.2:12345
\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.1:12345
\[debug\] .*? closing unused fd:\d+ listening on 127\.0\.0\.2:12345
/
=== TEST 3: does not close any fd if 'reuseport' is not used
--- http_config
server {
listen 127.0.0.1:12345;
}
--- config
location /t {
return 200;
}
--- request
GET /t
--- ignore_response_body
--- no_error_log
[error]
closing unused fd

View File

@ -437,6 +437,10 @@ echo "$info_txt applying the socket_cloexec patch for nginx"
patch -p1 < $root/patches/nginx-$main_ver-socket_cloexec.patch || exit 1
echo
echo "$info_txt applying the reuseport_close_unused_fds patch for nginx"
patch -p1 < $root/patches/nginx-$main_ver-reuseport_close_unused_fds.patch || exit 1
echo
cp $root/html/index.html docs/html/ || exit 1
cp $root/html/50x.html docs/html/ || exit 1