# HG changeset patch # User Roman Arutyunyan <a...@nginx.com> # Date 1693218974 -14400 # Mon Aug 28 14:36:14 2023 +0400 # Node ID 42c3166b675a6a7624bdf3d78c0d4685ce8172e3 # Parent fdad00808cb485ab83e919be59e9211ef06ac56a Added "max_shutdown_workers" directive.
The directive sets maximum number of workers in shutdown state while reloading nginx configuration. Upon reaching this value, workers restart is postponed until shutdown workers start exiting. This allows for lower peak resource consumption at the cost of slower reconfiguration. diff --git a/src/core/nginx.c b/src/core/nginx.c --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -83,6 +83,13 @@ static ngx_command_t ngx_core_commands[ 0, NULL }, + { ngx_string("max_shutdown_workers"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_core_conf_t, max_shutdown_workers), + NULL }, + { ngx_string("debug_points"), NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -1117,6 +1124,7 @@ ngx_core_module_create_conf(ngx_cycle_t ccf->shutdown_timeout = NGX_CONF_UNSET_MSEC; ccf->worker_processes = NGX_CONF_UNSET; + ccf->max_shutdown_workers = NGX_CONF_UNSET; ccf->debug_points = NGX_CONF_UNSET; ccf->rlimit_nofile = NGX_CONF_UNSET; @@ -1146,6 +1154,7 @@ ngx_core_module_init_conf(ngx_cycle_t *c ngx_conf_init_msec_value(ccf->shutdown_timeout, 0); ngx_conf_init_value(ccf->worker_processes, 1); + ngx_conf_init_value(ccf->max_shutdown_workers, 0); ngx_conf_init_value(ccf->debug_points, 0); #if (NGX_HAVE_CPU_AFFINITY) diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -94,6 +94,7 @@ typedef struct { ngx_msec_t shutdown_timeout; ngx_int_t worker_processes; + ngx_int_t max_shutdown_workers; ngx_int_t debug_points; ngx_int_t rlimit_nofile; diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c --- a/src/os/unix/ngx_process.c +++ b/src/os/unix/ngx_process.c @@ -216,6 +216,7 @@ ngx_spawn_process(ngx_cycle_t *cycle, ng ngx_processes[s].data = data; ngx_processes[s].name = name; ngx_processes[s].exiting = 0; + ngx_processes[s].old = 0; switch (respawn) { diff --git a/src/os/unix/ngx_process.h b/src/os/unix/ngx_process.h --- a/src/os/unix/ngx_process.h +++ b/src/os/unix/ngx_process.h @@ -33,6 +33,7 @@ typedef struct { unsigned detached:1; unsigned exiting:1; unsigned exited:1; + unsigned old:1; } ngx_process_t; diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c --- a/src/os/unix/ngx_process_cycle.c +++ b/src/os/unix/ngx_process_cycle.c @@ -15,6 +15,7 @@ static void ngx_start_worker_processes(n ngx_int_t type); static void ngx_start_worker_process(ngx_cycle_t *cycle, ngx_int_t i, ngx_int_t type); +static void ngx_restart_worker_processes(ngx_cycle_t *cycle); static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn); static void ngx_pass_open_channel(ngx_cycle_t *cycle); @@ -22,6 +23,7 @@ static void ngx_signal_worker_processes( static void ngx_signal_worker_process(ngx_cycle_t *cycle, ngx_int_t i, int signo); static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle); +static ngx_uint_t ngx_restart_worker_process(ngx_cycle_t *cycle); static void ngx_master_process_exit(ngx_cycle_t *cycle); static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data); static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker); @@ -235,8 +237,8 @@ ngx_master_process_cycle(ngx_cycle_t *cy ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); - ngx_start_worker_processes(cycle, ccf->worker_processes, - NGX_PROCESS_JUST_RESPAWN); + ngx_restart_worker_processes(cycle); + ngx_start_cache_manager_processes(cycle, 1); /* allow new processes to start */ @@ -360,6 +362,70 @@ ngx_start_worker_process(ngx_cycle_t *cy static void +ngx_restart_worker_processes(ngx_cycle_t *cycle) +{ + ngx_int_t i, n, m, worker; + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + n = 0; + m = ccf->max_shutdown_workers; + + if (m == 0) { + goto start; + } + + for (i = 0; i < ngx_last_process; i++) { + + if (ngx_processes[i].pid == -1 + || ngx_processes[i].proc != ngx_worker_process_cycle) + { + continue; + } + + ngx_processes[i].old = 0; + worker = (intptr_t) ngx_processes[i].data; + + if (!ngx_processes[i].exiting && worker < ccf->worker_processes) { + n++; + + } else if (m > 0) { + m--; + } + } + + n = (n > m) ? n - m : 0; + + if (n == 0) { + goto start; + } + + for (i = 0; i < ngx_last_process; i++) { + + if (ngx_processes[i].pid == -1 + || ngx_processes[i].proc != ngx_worker_process_cycle) + { + continue; + } + + ngx_processes[i].old = 1; + worker = (intptr_t) ngx_processes[i].data; + + if (!ngx_processes[i].exiting && worker < n) { + ngx_processes[i].just_spawn = 1; + } + } + +start: + + for (i = n; i < ccf->worker_processes; i++) { + ngx_start_worker_process(cycle, i, NGX_PROCESS_JUST_RESPAWN); + } +} + + +static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn) { ngx_uint_t i, manager, loader; @@ -486,15 +552,16 @@ ngx_signal_worker_process(ngx_cycle_t *c ch.fd = -1; - ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - "child: %i %P e:%d t:%d d:%d r:%d j:%d", + ngx_log_debug8(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "child: %i %P e:%d t:%d d:%d r:%d j:%d o:%d", i, ngx_processes[i].pid, ngx_processes[i].exiting, ngx_processes[i].exited, ngx_processes[i].detached, ngx_processes[i].respawn, - ngx_processes[i].just_spawn); + ngx_processes[i].just_spawn, + ngx_processes[i].old); if (ngx_processes[i].detached || ngx_processes[i].pid == -1) { return; @@ -563,15 +630,16 @@ ngx_reap_children(ngx_cycle_t *cycle) live = 0; for (i = 0; i < ngx_last_process; i++) { - ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - "child: %i %P e:%d t:%d d:%d r:%d j:%d", + ngx_log_debug8(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "child: %i %P e:%d t:%d d:%d r:%d j:%d o:%d", i, ngx_processes[i].pid, ngx_processes[i].exiting, ngx_processes[i].exited, ngx_processes[i].detached, ngx_processes[i].respawn, - ngx_processes[i].just_spawn); + ngx_processes[i].just_spawn, + ngx_processes[i].old); if (ngx_processes[i].pid == -1) { continue; @@ -660,6 +728,16 @@ ngx_reap_children(ngx_cycle_t *cycle) ngx_processes[i].pid = -1; } + if (ngx_processes[i].old + && ngx_processes[i].exiting + && !ngx_terminate + && !ngx_quit) + { + if (ngx_restart_worker_process(cycle)) { + live = 1; + } + } + } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) { live = 1; } @@ -669,6 +747,53 @@ ngx_reap_children(ngx_cycle_t *cycle) } +static ngx_uint_t +ngx_restart_worker_process(ngx_cycle_t *cycle) +{ + ngx_int_t i, j, m; + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + j = -1; + m = ccf->max_shutdown_workers; + + if (m == 0) { + return 0; + } + + for (i = 0; i < ngx_last_process; i++) { + + if (ngx_processes[i].pid == -1 || !ngx_processes[i].old) { + continue; + } + + if (!ngx_processes[i].exiting && !ngx_processes[i].exited) { + j = i; + continue; + } + + if (--m == 0) { + return 0; + } + } + + if (j == -1) { + return 0; + } + + ngx_start_worker_process(cycle, (intptr_t) ngx_processes[j].data, + NGX_PROCESS_RESPAWN); + + ngx_msleep(100); + + ngx_signal_worker_process(cycle, j, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + + return 1; +} + + static void ngx_master_process_exit(ngx_cycle_t *cycle) { _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel