nghttpx: Add --worker-process-grace-shutdown-period option
This commit is contained in:
parent
3e25ee8181
commit
87bdc21667
|
@ -196,6 +196,7 @@ OPTIONS = [
|
||||||
"frontend-quic-secret-file",
|
"frontend-quic-secret-file",
|
||||||
"rlimit-memlock",
|
"rlimit-memlock",
|
||||||
"max-worker-processes",
|
"max-worker-processes",
|
||||||
|
"worker-process-grace-shutdown-period",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGVARS = [
|
LOGVARS = [
|
||||||
|
|
115
src/shrpx.cc
115
src/shrpx.cc
|
@ -202,7 +202,8 @@ struct WorkerProcess {
|
||||||
)
|
)
|
||||||
: loop(loop),
|
: loop(loop),
|
||||||
worker_pid(worker_pid),
|
worker_pid(worker_pid),
|
||||||
ipc_fd(ipc_fd)
|
ipc_fd(ipc_fd),
|
||||||
|
termination_deadline(0.)
|
||||||
#ifdef ENABLE_HTTP3
|
#ifdef ENABLE_HTTP3
|
||||||
,
|
,
|
||||||
quic_ipc_fd(quic_ipc_fd),
|
quic_ipc_fd(quic_ipc_fd),
|
||||||
|
@ -264,6 +265,7 @@ struct WorkerProcess {
|
||||||
struct ev_loop *loop;
|
struct ev_loop *loop;
|
||||||
pid_t worker_pid;
|
pid_t worker_pid;
|
||||||
int ipc_fd;
|
int ipc_fd;
|
||||||
|
ev_tstamp termination_deadline;
|
||||||
#ifdef ENABLE_HTTP3
|
#ifdef ENABLE_HTTP3
|
||||||
int quic_ipc_fd;
|
int quic_ipc_fd;
|
||||||
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
||||||
|
@ -278,6 +280,74 @@ namespace {
|
||||||
std::deque<std::unique_ptr<WorkerProcess>> worker_processes;
|
std::deque<std::unique_ptr<WorkerProcess>> worker_processes;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
ev_timer worker_process_grace_period_timer;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void worker_process_grace_period_timercb(struct ev_loop *loop, ev_timer *w,
|
||||||
|
int revents) {
|
||||||
|
auto now = ev_now(loop);
|
||||||
|
ev_tstamp next_repeat = 0.;
|
||||||
|
|
||||||
|
for (auto it = std::begin(worker_processes);
|
||||||
|
it != std::end(worker_processes);) {
|
||||||
|
auto &wp = *it;
|
||||||
|
if (!(wp->termination_deadline > 0.)) {
|
||||||
|
++it;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto d = wp->termination_deadline - now;
|
||||||
|
if (d > 0) {
|
||||||
|
if (!(next_repeat > 0.) || d < next_repeat) {
|
||||||
|
next_repeat = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(NOTICE) << "Deleting worker process pid=" << wp->worker_pid
|
||||||
|
<< " because its grace shutdown period is over";
|
||||||
|
|
||||||
|
it = worker_processes.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_repeat > 0.) {
|
||||||
|
w->repeat = next_repeat;
|
||||||
|
ev_timer_again(loop, w);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev_timer_stop(loop, w);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void worker_process_set_termination_deadline(WorkerProcess *wp,
|
||||||
|
struct ev_loop *loop) {
|
||||||
|
auto config = get_config();
|
||||||
|
|
||||||
|
if (!(config->worker_process_grace_shutdown_period > 0.)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp->termination_deadline =
|
||||||
|
ev_now(loop) + config->worker_process_grace_shutdown_period;
|
||||||
|
|
||||||
|
if (!ev_is_active(&worker_process_grace_period_timer)) {
|
||||||
|
worker_process_grace_period_timer.repeat =
|
||||||
|
config->worker_process_grace_shutdown_period;
|
||||||
|
|
||||||
|
ev_timer_again(loop, &worker_process_grace_period_timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void worker_process_add(std::unique_ptr<WorkerProcess> wp) {
|
void worker_process_add(std::unique_ptr<WorkerProcess> wp) {
|
||||||
worker_processes.push_back(std::move(wp));
|
worker_processes.push_back(std::move(wp));
|
||||||
|
@ -285,7 +355,7 @@ void worker_process_add(std::unique_ptr<WorkerProcess> wp) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void worker_process_remove(const WorkerProcess *wp) {
|
void worker_process_remove(const WorkerProcess *wp, struct ev_loop *loop) {
|
||||||
for (auto it = std::begin(worker_processes); it != std::end(worker_processes);
|
for (auto it = std::begin(worker_processes); it != std::end(worker_processes);
|
||||||
++it) {
|
++it) {
|
||||||
auto &s = *it;
|
auto &s = *it;
|
||||||
|
@ -295,6 +365,11 @@ void worker_process_remove(const WorkerProcess *wp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
worker_processes.erase(it);
|
worker_processes.erase(it);
|
||||||
|
|
||||||
|
if (worker_processes.empty()) {
|
||||||
|
ev_timer_stop(loop, &worker_process_grace_period_timer);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,22 +387,24 @@ void worker_process_adjust_limit() {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void worker_process_remove_all() {
|
void worker_process_remove_all(struct ev_loop *loop) {
|
||||||
std::deque<std::unique_ptr<WorkerProcess>>().swap(worker_processes);
|
std::deque<std::unique_ptr<WorkerProcess>>().swap(worker_processes);
|
||||||
|
|
||||||
|
ev_timer_stop(loop, &worker_process_grace_period_timer);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Send signal |signum| to all worker processes, and clears
|
// Send signal |signum| to all worker processes, and clears
|
||||||
// worker_processes.
|
// worker_processes.
|
||||||
void worker_process_kill(int signum) {
|
void worker_process_kill(int signum, struct ev_loop *loop) {
|
||||||
for (auto &s : worker_processes) {
|
for (auto &s : worker_processes) {
|
||||||
if (s->worker_pid == -1) {
|
if (s->worker_pid == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
kill(s->worker_pid, signum);
|
kill(s->worker_pid, signum);
|
||||||
}
|
}
|
||||||
worker_process_remove_all();
|
worker_process_remove_all(loop);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -646,13 +723,14 @@ void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
|
||||||
close(addr.fd);
|
close(addr.fd);
|
||||||
}
|
}
|
||||||
ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
||||||
|
worker_process_set_termination_deadline(wp, loop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case RELOAD_SIGNAL:
|
case RELOAD_SIGNAL:
|
||||||
reload_config(wp);
|
reload_config(wp);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
worker_process_kill(w->signum);
|
worker_process_kill(w->signum, loop);
|
||||||
ev_break(loop);
|
ev_break(loop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -667,7 +745,7 @@ void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
|
||||||
|
|
||||||
auto pid = wp->worker_pid;
|
auto pid = wp->worker_pid;
|
||||||
|
|
||||||
worker_process_remove(wp);
|
worker_process_remove(wp, loop);
|
||||||
|
|
||||||
if (worker_process_last_pid() == pid) {
|
if (worker_process_last_pid() == pid) {
|
||||||
ev_break(loop);
|
ev_break(loop);
|
||||||
|
@ -1482,7 +1560,7 @@ pid_t fork_worker_process(
|
||||||
|
|
||||||
// Remove all WorkerProcesses to stop any registered watcher on
|
// Remove all WorkerProcesses to stop any registered watcher on
|
||||||
// default loop.
|
// default loop.
|
||||||
worker_process_remove_all();
|
worker_process_remove_all(EV_DEFAULT);
|
||||||
|
|
||||||
close_unused_inherited_addr(iaddrs);
|
close_unused_inherited_addr(iaddrs);
|
||||||
|
|
||||||
|
@ -1657,6 +1735,9 @@ int event_loop() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ev_timer_init(&worker_process_grace_period_timer,
|
||||||
|
worker_process_grace_period_timercb, 0., 0.);
|
||||||
|
|
||||||
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd
|
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd
|
||||||
#ifdef ENABLE_HTTP3
|
#ifdef ENABLE_HTTP3
|
||||||
,
|
,
|
||||||
|
@ -1683,6 +1764,8 @@ int event_loop() {
|
||||||
|
|
||||||
ev_run(loop, 0);
|
ev_run(loop, 0);
|
||||||
|
|
||||||
|
ev_timer_stop(loop, &worker_process_grace_period_timer);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -3225,6 +3308,14 @@ Process:
|
||||||
value, the oldest worker process is terminated
|
value, the oldest worker process is terminated
|
||||||
immediately. Specifying 0 means no limit and it is the
|
immediately. Specifying 0 means no limit and it is the
|
||||||
default behaviour.
|
default behaviour.
|
||||||
|
--worker-process-grace-shutdown-period=<DURATION>
|
||||||
|
Maximum period for a worker process to terminate
|
||||||
|
gracefully. When a worker process enters in graceful
|
||||||
|
shutdown period (e.g., when nghttpx reloads its
|
||||||
|
configuration) and it does not finish handling the
|
||||||
|
existing connections in the given period of time, it is
|
||||||
|
immediately terminated. Specifying 0 means no limit and
|
||||||
|
it is the default behaviour.
|
||||||
|
|
||||||
Scripting:
|
Scripting:
|
||||||
--mruby-file=<PATH>
|
--mruby-file=<PATH>
|
||||||
|
@ -3783,6 +3874,7 @@ void reload_config(WorkerProcess *wp) {
|
||||||
// Send last worker process a graceful shutdown notice
|
// Send last worker process a graceful shutdown notice
|
||||||
auto &last_wp = worker_processes.back();
|
auto &last_wp = worker_processes.back();
|
||||||
ipc_send(last_wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
ipc_send(last_wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
||||||
|
worker_process_set_termination_deadline(last_wp.get(), loop);
|
||||||
// We no longer use signals for this worker.
|
// We no longer use signals for this worker.
|
||||||
last_wp->shutdown_signal_watchers();
|
last_wp->shutdown_signal_watchers();
|
||||||
|
|
||||||
|
@ -4128,6 +4220,8 @@ int main(int argc, char **argv) {
|
||||||
186},
|
186},
|
||||||
{SHRPX_OPT_RLIMIT_MEMLOCK.c_str(), required_argument, &flag, 187},
|
{SHRPX_OPT_RLIMIT_MEMLOCK.c_str(), required_argument, &flag, 187},
|
||||||
{SHRPX_OPT_MAX_WORKER_PROCESSES.c_str(), required_argument, &flag, 188},
|
{SHRPX_OPT_MAX_WORKER_PROCESSES.c_str(), required_argument, &flag, 188},
|
||||||
|
{SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD.c_str(),
|
||||||
|
required_argument, &flag, 189},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -5023,6 +5117,11 @@ int main(int argc, char **argv) {
|
||||||
// --max-worker-processes
|
// --max-worker-processes
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_MAX_WORKER_PROCESSES, StringRef{optarg});
|
cmdcfgs.emplace_back(SHRPX_OPT_MAX_WORKER_PROCESSES, StringRef{optarg});
|
||||||
break;
|
break;
|
||||||
|
case 189:
|
||||||
|
// --worker-process-grace-shutdown-period
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD,
|
||||||
|
StringRef{optarg});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2666,6 +2666,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||||
break;
|
break;
|
||||||
case 36:
|
case 36:
|
||||||
switch (name[35]) {
|
switch (name[35]) {
|
||||||
|
case 'd':
|
||||||
|
if (util::strieq_l("worker-process-grace-shutdown-perio", name, 35)) {
|
||||||
|
return SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
|
if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
|
||||||
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
|
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
|
||||||
|
@ -4144,6 +4149,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
}
|
}
|
||||||
case SHRPX_OPTID_MAX_WORKER_PROCESSES:
|
case SHRPX_OPTID_MAX_WORKER_PROCESSES:
|
||||||
return parse_uint(&config->max_worker_processes, opt, optarg);
|
return parse_uint(&config->max_worker_processes, opt, optarg);
|
||||||
|
case SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD:
|
||||||
|
return parse_duration(&config->worker_process_grace_shutdown_period, opt,
|
||||||
|
optarg);
|
||||||
case SHRPX_OPTID_CONF:
|
case SHRPX_OPTID_CONF:
|
||||||
LOG(WARN) << "conf: ignored";
|
LOG(WARN) << "conf: ignored";
|
||||||
|
|
||||||
|
|
|
@ -398,6 +398,8 @@ constexpr auto SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE =
|
||||||
constexpr auto SHRPX_OPT_RLIMIT_MEMLOCK = StringRef::from_lit("rlimit-memlock");
|
constexpr auto SHRPX_OPT_RLIMIT_MEMLOCK = StringRef::from_lit("rlimit-memlock");
|
||||||
constexpr auto SHRPX_OPT_MAX_WORKER_PROCESSES =
|
constexpr auto SHRPX_OPT_MAX_WORKER_PROCESSES =
|
||||||
StringRef::from_lit("max-worker-processes");
|
StringRef::from_lit("max-worker-processes");
|
||||||
|
constexpr auto SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD =
|
||||||
|
StringRef::from_lit("worker-process-grace-shutdown-period");
|
||||||
|
|
||||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||||
|
|
||||||
|
@ -1078,7 +1080,8 @@ struct Config {
|
||||||
single_thread{false},
|
single_thread{false},
|
||||||
ignore_per_pattern_mruby_error{false},
|
ignore_per_pattern_mruby_error{false},
|
||||||
ev_loop_flags{0},
|
ev_loop_flags{0},
|
||||||
max_worker_processes{0} {
|
max_worker_processes{0},
|
||||||
|
worker_process_grace_shutdown_period{0.} {
|
||||||
}
|
}
|
||||||
~Config();
|
~Config();
|
||||||
|
|
||||||
|
@ -1133,6 +1136,7 @@ struct Config {
|
||||||
// flags passed to ev_default_loop() and ev_loop_new()
|
// flags passed to ev_default_loop() and ev_loop_new()
|
||||||
int ev_loop_flags;
|
int ev_loop_flags;
|
||||||
size_t max_worker_processes;
|
size_t max_worker_processes;
|
||||||
|
ev_tstamp worker_process_grace_shutdown_period;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Config *get_config();
|
const Config *get_config();
|
||||||
|
@ -1330,6 +1334,7 @@ enum {
|
||||||
SHRPX_OPTID_VERIFY_CLIENT_CACERT,
|
SHRPX_OPTID_VERIFY_CLIENT_CACERT,
|
||||||
SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED,
|
SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED,
|
||||||
SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
|
SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
|
||||||
|
SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD,
|
||||||
SHRPX_OPTID_WORKER_READ_BURST,
|
SHRPX_OPTID_WORKER_READ_BURST,
|
||||||
SHRPX_OPTID_WORKER_READ_RATE,
|
SHRPX_OPTID_WORKER_READ_RATE,
|
||||||
SHRPX_OPTID_WORKER_WRITE_BURST,
|
SHRPX_OPTID_WORKER_WRITE_BURST,
|
||||||
|
|
Loading…
Reference in New Issue