Skip to content

Commit f059d4a

Browse files
committed
remember worker_count passed from configuration
tokio's metric APIs is not stablized yet: tokio-rs/tokio#4073
1 parent 9e13020 commit f059d4a

File tree

10 files changed

+113
-77
lines changed

10 files changed

+113
-77
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ libc = "0.2"
161161

162162
futures = "0.3"
163163
tokio = { version = "1", features = ["rt", "signal"] }
164+
num_cpus = "1.13"
164165

165166
ipnet = { version = "2.3", optional = true }
166167

crates/shadowsocks-service/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ async-trait = "0.1"
110110

111111
socket2 = { version = "0.4", features = ["all"] }
112112
libc = "0.2"
113-
num_cpus = "1.13"
114113

115114
hyper = { version = "0.14.16", optional = true, features = ["full"] }
116115
tower = { version = "0.4", optional = true }

crates/shadowsocks-service/src/config.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,11 @@ pub struct Config {
10821082
/// Configuration file path, the actual path of the configuration.
10831083
/// This is normally for auto-reloading if implementation supports.
10841084
pub config_path: Option<PathBuf>,
1085+
1086+
#[doc(hidden)]
1087+
/// Workers in runtime
1088+
/// It should be replaced with metrics APIs: https://github.com/tokio-rs/tokio/issues/4073
1089+
pub worker_count: usize,
10851090
}
10861091

10871092
/// Configuration parsing error kind
@@ -1192,6 +1197,8 @@ impl Config {
11921197
balancer: BalancerConfig::default(),
11931198

11941199
config_path: None,
1200+
1201+
worker_count: 1,
11951202
}
11961203
}
11971204

crates/shadowsocks-service/src/manager/server.rs

+12
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pub struct Manager {
8282
acl: Option<Arc<AccessControl>>,
8383
ipv6_first: bool,
8484
security: SecurityConfig,
85+
worker_count: usize,
8586
}
8687

8788
impl Manager {
@@ -103,6 +104,7 @@ impl Manager {
103104
acl: None,
104105
ipv6_first: false,
105106
security: SecurityConfig::default(),
107+
worker_count: 1,
106108
}
107109
}
108110

@@ -152,6 +154,14 @@ impl Manager {
152154
self.security = security;
153155
}
154156

157+
/// Set runtime worker count
158+
///
159+
/// Should be replaced with tokio's metric API when it is stablized.
160+
/// https://github.com/tokio-rs/tokio/issues/4073
161+
pub fn set_worker_count(&mut self, worker_count: usize) {
162+
self.worker_count = worker_count;
163+
}
164+
155165
/// Start serving
156166
pub async fn run(self) -> io::Result<()> {
157167
let mut listener = ManagerListener::bind(&self.context, &self.svr_cfg.addr).await?;
@@ -235,6 +245,8 @@ impl Manager {
235245

236246
server.set_security_config(&self.security);
237247

248+
server.set_worker_count(self.worker_count);
249+
238250
let server_port = server.config().addr().port();
239251

240252
let mut servers = self.servers.lock().await;

crates/shadowsocks-service/src/server/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ pub async fn run(config: Config) -> io::Result<()> {
121121
server.set_ipv6_first(config.ipv6_first);
122122
}
123123

124+
if config.worker_count >= 1 {
125+
server.set_worker_count(config.worker_count);
126+
}
127+
124128
server.set_security_config(&config.security);
125129

126130
servers.push(server);

crates/shadowsocks-service/src/server/server.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct Server {
3030
udp_capacity: Option<usize>,
3131
manager_addr: Option<ManagerAddr>,
3232
accept_opts: AcceptOpts,
33+
worker_count: usize,
3334
}
3435

3536
impl Server {
@@ -47,6 +48,7 @@ impl Server {
4748
udp_capacity: None,
4849
manager_addr: None,
4950
accept_opts: AcceptOpts::default(),
51+
worker_count: 1,
5052
}
5153
}
5254

@@ -81,6 +83,14 @@ impl Server {
8183
self.manager_addr = Some(manager_addr);
8284
}
8385

86+
/// Set runtime worker count
87+
///
88+
/// Should be replaced with tokio's metric API when it is stablized.
89+
/// https://github.com/tokio-rs/tokio/issues/4073
90+
pub fn set_worker_count(&mut self, worker_count: usize) {
91+
self.worker_count = worker_count;
92+
}
93+
8494
/// Get server's configuration
8595
pub fn config(&self) -> &ServerConfig {
8696
&self.svr_cfg
@@ -169,13 +179,14 @@ impl Server {
169179
}
170180

171181
async fn run_udp_server(&self) -> io::Result<()> {
172-
let server = UdpServer::new(
182+
let mut server = UdpServer::new(
173183
self.context.clone(),
174184
self.svr_cfg.method(),
175185
self.udp_expiry_duration,
176186
self.udp_capacity,
177187
self.accept_opts.clone(),
178188
);
189+
server.set_worker_count(self.worker_count);
179190
server.run(&self.svr_cfg).await
180191
}
181192

crates/shadowsocks-service/src/server/udprelay.rs

+66-74
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pub struct UdpServer {
8686
keepalive_rx: mpsc::Receiver<NatKey>,
8787
time_to_live: Duration,
8888
accept_opts: AcceptOpts,
89+
worker_count: usize,
8990
}
9091

9192
impl UdpServer {
@@ -127,9 +128,15 @@ impl UdpServer {
127128
keepalive_rx,
128129
time_to_live,
129130
accept_opts,
131+
worker_count: 1,
130132
}
131133
}
132134

135+
#[inline]
136+
pub fn set_worker_count(&mut self, worker_count: usize) {
137+
self.worker_count = worker_count;
138+
}
139+
133140
pub async fn run(mut self, svr_cfg: &ServerConfig) -> io::Result<()> {
134141
let socket = ProxySocket::bind_with_opts(self.context.context(), svr_cfg, self.accept_opts.clone()).await?;
135142

@@ -145,63 +152,35 @@ impl UdpServer {
145152

146153
let mut orx_opt = None;
147154

148-
let cpus = num_cpus::get();
149-
let mut other_cores = Vec::new();
155+
let cpus = self.worker_count;
156+
let mut other_receivers = Vec::new();
150157
if cpus > 1 {
151-
let (otx, orx) = mpsc::channel(64);
158+
let (otx, orx) = mpsc::channel((cpus - 1) * 16);
152159
orx_opt = Some(orx);
153160

154-
other_cores.reserve(cpus - 1);
161+
other_receivers.reserve(cpus - 1);
155162
trace!("udp server starting extra {} recv workers", cpus - 1);
156163

157164
for _ in 1..cpus {
158165
let otx = otx.clone();
159166
let listener = listener.clone();
160167
let context = self.context.clone();
161168

162-
other_cores.push(tokio::spawn(async move {
169+
other_receivers.push(tokio::spawn(async move {
163170
let mut buffer = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE];
164171

165172
loop {
166-
let (n, peer_addr, target_addr, control) = match listener.recv_from_with_ctrl(&mut buffer).await
167-
{
168-
Ok(s) => s,
169-
Err(err) => {
170-
error!("udp server recv_from failed with error: {}", err);
171-
continue;
172-
}
173-
};
174-
175-
if n == 0 {
176-
// For windows, it will generate a ICMP Port Unreachable Message
177-
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-recvfrom
178-
// Which will result in recv_from return 0.
179-
//
180-
// It cannot be solved here, because `WSAGetLastError` is already set.
181-
//
182-
// See `relay::udprelay::utils::create_socket` for more detail.
183-
continue;
184-
}
185-
186-
if context.check_client_blocked(&peer_addr) {
187-
warn!(
188-
"udp client {} outbound {} access denied by ACL rules",
189-
peer_addr, target_addr
190-
);
191-
continue;
192-
}
173+
let (n, peer_addr, target_addr, control) =
174+
match UdpServer::recv_one_packet(&context, &listener, &mut buffer).await {
175+
Some(s) => s,
176+
None => continue,
177+
};
193178

194-
if context.check_outbound_blocked(&target_addr).await {
195-
warn!("udp client {} outbound {} blocked by ACL rules", peer_addr, target_addr);
196-
continue;
197-
}
198-
199-
let r = otx
179+
if let Err(..) = otx
200180
.send((peer_addr, target_addr, control, Bytes::copy_from_slice(&buffer[..n])))
201-
.await;
202-
203-
// If Result is error, the channel receiver is closed. We should exit the task.
204-
if r.is_err() {
181+
.await
182+
{
183+
// If Result is error, the channel receiver is closed. We should exit the task.
205184
break;
206185
}
207186
}
@@ -222,7 +201,7 @@ impl UdpServer {
222201
}
223202

224203
let _guard = MulticoreTaskGuard {
225-
tasks: &mut other_cores,
204+
tasks: &mut other_receivers,
226205
};
227206

228207
#[inline]
@@ -251,39 +230,12 @@ impl UdpServer {
251230
self.assoc_map.keep_alive(&peer_addr);
252231
}
253232

254-
recv_result = listener.recv_from_with_ctrl(&mut buffer) => {
233+
recv_result = UdpServer::recv_one_packet(&self.context, &listener, &mut buffer) => {
255234
let (n, peer_addr, target_addr, control) = match recv_result {
256-
Ok(s) => s,
257-
Err(err) => {
258-
error!("udp server recv_from failed with error: {}", err);
259-
continue;
260-
}
235+
Some(s) => s,
236+
None => continue,
261237
};
262238

263-
if n == 0 {
264-
// For windows, it will generate a ICMP Port Unreachable Message
265-
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-recvfrom
266-
// Which will result in recv_from return 0.
267-
//
268-
// It cannot be solved here, because `WSAGetLastError` is already set.
269-
//
270-
// See `relay::udprelay::utils::create_socket` for more detail.
271-
continue;
272-
}
273-
274-
if self.context.check_client_blocked(&peer_addr) {
275-
warn!(
276-
"udp client {} outbound {} access denied by ACL rules",
277-
peer_addr, target_addr
278-
);
279-
continue;
280-
}
281-
282-
if self.context.check_outbound_blocked(&target_addr).await {
283-
warn!("udp client {} outbound {} blocked by ACL rules", peer_addr, target_addr);
284-
continue;
285-
}
286-
287239
let data = &buffer[..n];
288240
if let Err(err) = self.send_packet(&listener, peer_addr, target_addr, control, Bytes::copy_from_slice(data)).await {
289241
debug!(
@@ -295,7 +247,7 @@ impl UdpServer {
295247
}
296248
}
297249

298-
recv_result = multicore_recv(&mut orx_opt) => {
250+
recv_result = multicore_recv(&mut orx_opt), if orx_opt.is_some() => {
299251
let (peer_addr, target_addr, control, data) = recv_result;
300252
let data_len = data.len();
301253
if let Err(err) = self.send_packet(&listener, peer_addr, target_addr, control, data).await {
@@ -311,6 +263,46 @@ impl UdpServer {
311263
}
312264
}
313265

266+
async fn recv_one_packet(
267+
context: &ServiceContext,
268+
l: &MonProxySocket,
269+
buffer: &mut [u8],
270+
) -> Option<(usize, SocketAddr, Address, Option<UdpSocketControlData>)> {
271+
let (n, peer_addr, target_addr, control) = match l.recv_from_with_ctrl(buffer).await {
272+
Ok(s) => s,
273+
Err(err) => {
274+
error!("udp server recv_from failed with error: {}", err);
275+
return None;
276+
}
277+
};
278+
279+
if n == 0 {
280+
// For windows, it will generate a ICMP Port Unreachable Message
281+
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-recvfrom
282+
// Which will result in recv_from return 0.
283+
//
284+
// It cannot be solved here, because `WSAGetLastError` is already set.
285+
//
286+
// See `relay::udprelay::utils::create_socket` for more detail.
287+
return None;
288+
}
289+
290+
if context.check_client_blocked(&peer_addr) {
291+
warn!(
292+
"udp client {} outbound {} access denied by ACL rules",
293+
peer_addr, target_addr
294+
);
295+
return None;
296+
}
297+
298+
if context.check_outbound_blocked(&target_addr).await {
299+
warn!("udp client {} outbound {} blocked by ACL rules", peer_addr, target_addr);
300+
return None;
301+
}
302+
303+
Some((n, peer_addr, target_addr, control))
304+
}
305+
314306
async fn send_packet(
315307
&mut self,
316308
listener: &Arc<MonProxySocket>,

src/service/manager.rs

+5
Original file line numberDiff line numberDiff line change
@@ -451,18 +451,23 @@ pub fn main(matches: &ArgMatches) {
451451

452452
info!("shadowsocks manager {} build {}", crate::VERSION, crate::BUILD_TIME);
453453

454+
let mut worker_count = 1;
454455
let mut builder = match service_config.runtime.mode {
455456
RuntimeMode::SingleThread => Builder::new_current_thread(),
456457
#[cfg(feature = "multi-threaded")]
457458
RuntimeMode::MultiThread => {
458459
let mut builder = Builder::new_multi_thread();
459460
if let Some(worker_threads) = service_config.runtime.worker_count {
461+
worker_count = worker_threads;
460462
builder.worker_threads(worker_threads);
463+
} else {
464+
worker_count = num_cpus::get();
461465
}
462466

463467
builder
464468
}
465469
};
470+
config.worker_count = worker_count;
466471

467472
let runtime = builder.enable_all().build().expect("create tokio Runtime");
468473

0 commit comments

Comments
 (0)