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