1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use ntex_util::time::Millis;
use crate::{Affinity, LoadBalancing, Server, ServerConfiguration, manager::ServerManager};
const DEFAULT_SHUTDOWN_TIMEOUT: Millis = Millis::from_secs(30);
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
/// Builder for a pool of server workers.
pub struct WorkerPool {
pub(crate) num: usize,
pub(crate) name: String,
pub(crate) no_signals: bool,
pub(crate) stop_runtime: bool,
pub(crate) stop_on_panic: bool,
pub(crate) graceful_shutdown: bool,
pub(crate) graceful_shutdown_timeout: Millis,
pub(crate) affinity: Affinity,
pub(crate) balancing: LoadBalancing,
}
impl Default for WorkerPool {
fn default() -> Self {
Self::new()
}
}
impl WorkerPool {
#[must_use]
/// Creates a worker pool with default settings.
pub fn new() -> Self {
let num = crate::affinity::cpu_count();
WorkerPool {
num,
name: "ntex".to_string(),
no_signals: false,
stop_runtime: false,
stop_on_panic: false,
graceful_shutdown: false,
graceful_shutdown_timeout: DEFAULT_SHUTDOWN_TIMEOUT,
affinity: Affinity::default(),
balancing: LoadBalancing::IncomingCpu,
}
}
#[must_use]
/// Sets the worker thread name prefix.
///
/// The configured name is used for worker thread names.
pub fn name<T: AsRef<str>>(mut self, name: T) -> Self {
self.name = name.as_ref().to_string();
self
}
#[must_use]
/// Sets the number of worker threads to start.
///
/// By default, the server uses the number of available logical CPUs.
///
/// # Panics
///
/// Panics if `num` is zero. A server without workers would never become
/// ready or process anything.
pub fn workers(mut self, num: usize) -> Self {
assert!(num > 0, "Server should have at least one worker");
self.num = num;
self
}
#[must_use]
/// Stops the current ntex runtime after the server has stopped.
///
/// By default, "stop runtime" is disabled.
pub fn stop_runtime(mut self) -> Self {
self.stop_runtime = true;
self
}
#[must_use]
/// Stops the server when one of the workers fails.
///
/// A worker fails when it panics or its service cannot be created. The
/// stop is graceful only if [`graceful_shutdown`](Self::graceful_shutdown)
/// is enabled. Without this option, a failed worker is restarted.
///
/// By default, "stop on panic" is disabled.
pub fn stop_on_panic(mut self) -> Self {
self.stop_on_panic = true;
self
}
#[must_use]
/// Disables signal handling.
///
/// By default, the server stops on SIGINT, SIGTERM, and SIGQUIT.
pub fn disable_signals(mut self) -> Self {
self.no_signals = true;
self
}
#[must_use]
/// Enables graceful shutdown on SIGQUIT, fatal signals, and panics.
///
/// When enabled, SIGQUIT, SIGSEGV, SIGABRT, application panics, and
/// worker failures with "stop on panic" stop the server gracefully.
/// SIGTERM always stops gracefully and SIGINT always stops immediately.
/// SIGSEGV and SIGABRT require runtime fatal signal handling, application
/// panics require runtime panic handling.
///
/// By default, these events stop the server immediately.
pub fn graceful_shutdown(mut self) -> Self {
self.graceful_shutdown = true;
self
}
#[must_use]
/// Timeout for graceful worker shutdown.
///
/// After receiving a stop signal, workers have this much time to finish
/// serving requests. Workers that are still alive after the timeout are
/// forcefully dropped.
///
/// This bounds the worker as a whole, not an individual connection. Each
/// connection is bound separately by `IoConfig::set_shutdown_timeout`, so
/// this value should leave room for the connections a worker is still
/// draining to shut down themselves.
///
/// By default, the timeout is set to 30 seconds.
pub fn graceful_shutdown_timeout<T: Into<Millis>>(mut self, timeout: T) -> Self {
self.graceful_shutdown_timeout = timeout.into();
self
}
#[must_use]
#[deprecated(
since = "4.3.0",
note = "affinity is enabled by default, use `affinity()` to change it"
)]
/// Does nothing, affinity is enabled by default.
pub fn enable_affinity(self) -> Self {
self
}
#[must_use]
/// Sets the CPU affinity of worker threads.
///
/// Workers are spread over physical cores first, see [`Affinity`].
///
/// By default, [`Affinity::Soft`] is used on Windows and
/// [`Affinity::Core`] on other platforms.
pub fn affinity(mut self, affinity: Affinity) -> Self {
self.affinity = affinity;
self
}
#[must_use]
/// Sets how items are distributed between workers.
///
/// By default, [`LoadBalancing::IncomingCpu`] sends new items to idle
/// workers, which helps when connections are long-lived or their load is
/// uneven, and prefers the worker on the CPU that receives a connection's
/// network packets. [`LoadBalancing::RoundRobin`] sends them to the
/// workers in turn.
pub fn load_balancing(mut self, balancing: LoadBalancing) -> Self {
self.balancing = balancing;
self
}
/// Starts processing incoming items and returns a server controller.
pub fn run<F: ServerConfiguration>(self, factory: F) -> Server<F::Item> {
ServerManager::start(self, factory)
}
}