我有一个用 Rust 编写的小型网络应用程序服务器,代码 is available here .
一切正常,但是在积极使用“一段时间”后,进程 CPU 使用率将达到 100%(如 top
所报告)并一直保持到重新启动。
问题是我无法弄清楚是什么导致了这种 CPU 使用率,也不知道如何有效地调试它 - 主要是因为我无法在本地重新创建 CPU 使用率。它发生在 FreeBSD(直接访问进程)和运行 Ubuntu 18.10 的新服务器(通过 Nginx 反向代理)上;但是我无法在运行 macOS 的本地计算机上重新创建它(可能是由于操作系统,但可能只是访问方式不同
如果我运行 perf record
,报告会显示:
Samples: 44K of event 'cpu-clock', Event count (approx.): 11073000000
Overhead Command Shared Object Symbol
21.92% juke [kernel.kallsyms] [k] do_syscall_64
17.52% juke libpthread-2.23.so [.] __libc_recv
5.53% juke [kernel.kallsyms] [k] inet_diag_table+0x800000004df0
4.13% juke [kernel.kallsyms] [k] tcp_recvmsg
3.59% juke [kernel.kallsyms] [k] __fget
3.31% juke [kernel.kallsyms] [k] inet_recvmsg
3.22% juke [kernel.kallsyms] [k] seccomp_run_filters
3.10% juke [kernel.kallsyms] [k] aa_profile_af_perm
3.05% juke [kernel.kallsyms] [k] aa_label_sk_perm
2.27% juke [kernel.kallsyms] [k] aa_sk_perm
1.94% juke [kernel.kallsyms] [k] __sys_recvfrom
1.75% juke [kernel.kallsyms] [k] tcp_release_cb
1.74% juke [kernel.kallsyms] [k] __local_bh_enable_ip
1.74% juke [kernel.kallsyms] [k] syscall_trace_enter
1.74% juke [kernel.kallsyms] [k] __x64_sys_recvfrom
1.63% juke libpthread-2.23.so [.] __pthread_enable_asynccancel
1.54% juke [kernel.kallsyms] [k] release_sock
1.44% juke [kernel.kallsyms] [k] __seccomp_filter
1.34% juke libpthread-2.23.so [.] __pthread_disable_asynccancel
1.31% juke juke [.] _ZN88_$LT$tiny_http..util..sequential..SequentialReader$LT$R$GT$$u20$as$u20$std..io..Read$GT$4read17h1ab62509f0eccd24E
1.23% juke juke [.] _ZN71_$LT$std..io..buffered..BufReader$LT$R$GT$$u20$as$u20$std..io..Read$GT$4read17he719b00617f471fdE
1.18% juke juke [.] _ZN91_$LT$rouille..websocket..websocket..Websocket$u20$as$u20$core..iter..iterator..Iterator$GT$4next17h0096bf051fcbfa61E
1.09% juke [kernel.kallsyms] [k] import_single_range
0.98% juke [kernel.kallsyms] [k] aa_label_next_confined
0.94% juke [kernel.kallsyms] [k] sock_recvmsg
0.76% juke [kernel.kallsyms] [k] apparmor_socket_recvmsg
0.75% juke [kernel.kallsyms] [k] _raw_spin_lock_bh
0.70% juke [kernel.kallsyms] [k] __fget_light
0.63% juke [kernel.kallsyms] [k] __indirect_thunk_start
0.61% juke [kernel.kallsyms] [k] rcu_all_qs
0.55% juke juke [.] _ZN96_$LT$tiny_http..util..custom_stream..CustomStream$LT$R$C$$u20$W$GT$$u20$as$u20$std..io..Read$GT$4read17h4875891095ddc7f7E
0.50% juke [kernel.kallsyms] [k] security_socket_recvmsg
0.44% juke [kernel.kallsyms] [k] _raw_spin_unlock_bh
..如果我将 gdb 附加到它,就会有一堆线程(大约 15 个?)正在执行此操作:
#0 0x00007f41cf58087f in recv () from target:/lib/x86_64-linux-gnu/libpthread.so.0
#1 0x00005598b9ef49ba in recv_with_flags () at src/libstd/sys/unix/net.rs:228
#2 read () at src/libstd/sys/unix/net.rs:237
#3 read () at src/libstd/sys_common/net.rs:255
#4 read () at src/libstd/net/tcp.rs:570
#5 0x00005598b9c3d6dc in <std::io::buffered::BufReader<R> as std::io::Read>::read ()
#6 0x00005598b9c30441 in <tiny_http::util::sequential::SequentialReader<R> as std::io::Read>::read ()
#7 0x00005598b9be94c2 in <tiny_http::util::custom_stream::CustomStream<R, W> as std::io::Read>::read ()
#8 0x00005598b9c0caad in <rouille::websocket::websocket::Websocket as core::iter::traits::iterator::Iterator>::next ()
#9 0x00005598b9bcf839 in juke::web::websocket_handling_thread ()
#10 0x00005598b9bc67b4 in std::sys_common::backtrace::__rust_begin_short_backtrace ()
#11 0x00005598b9bb109c in std::panicking::try::do_call ()
#12 0x00005598b9f0113a in __rust_maybe_catch_panic () at src/libpanic_unwind/lib.rs:87
#13 0x00005598b9bba0b0 in <F as alloc::boxed::FnBox<A>>::call_box ()
#14 0x00005598b9f004ce in call_once<(),()> () at /rustc/fc50f328b0353b285421b8ff5d4100966387a997/src/liballoc/boxed.rs:759
#15 start_thread () at src/libstd/sys_common/thread.rs:14
#16 thread_start () at src/libstd/sys/unix/thread.rs:81
#17 0x00007f41cf5776ba in start_thread () from target:/lib/x86_64-linux-gnu/libpthread.so.0
#18 0x00007f41cf09741d in clone () from target:/lib/x86_64-linux-gnu/libc.so.6
这个回溯指向我代码中的这个部分:
https://github.com/dbr/juke-rs/blob/f845e36402d7106dc4b2262adc165fb3e5f4c123/src/web.rs#L64
fn websocket_handling_thread(
mut websocket: websocket::Websocket,
global_status: &Arc<RwLock<PlaybackStatus>>,
global_queue: &Arc<RwLock<TheList>>,
) {
// We wait for a new message to come from the websocket.
while let Some(message) = websocket.next() {
..调用方式:
(GET) (/ws) => {
let (response, websocket) = try_or_400!(websocket::start(&request, Some("juke")));
let gs = global_status.clone();
let gq = global_queue.clone();
std::thread::spawn(move || {
let ws = websocket.recv().unwrap();
websocket_handling_thread(ws, &gs, &gq);
});
response
},
然而,这一切似乎都符合 Rouille 的示例 - 这是 Rouille(或底层 tiny_http 库)中的错误,还是我做错了什么?
最佳答案
正如@ecstaticm0rse 在评论中发现的那样,问题出在 Rouille 3.0.0 中,
fn next(&mut self) -> Option<Message> {
loop {
[...snip...]
let n = match self.socket.as_mut().unwrap().read(&mut buf) {
Ok(n) => n,
Err(ref err) if err.kind() == io::ErrorKind::Interrupted => 0,
Err(_) => {
self.socket = None;
return None;
},
};
[...snip...]
for element in self.state_machine.feed(&buf[0 .. n]) {
[...snip...]
如果套接字关闭(不是通过 websocket 协议(protocol)关闭,但在网络级别,例如设备失去连接)
由于n
设置为0,for element ...
in loop 因为0 .. n
范围,然后再次调用 read(...)
,然后循环返回调用 read()
(再次返回 Ok(0)
,并且,永远循环)
该错误的重现是使用像 websocat 这样的 websocket 客户端然后连接到服务器并用 sigint (ctrl+c) 杀死客户端几次。
我已经在 Rouille 问题跟踪器上报告了这个错误并创建了一个拉取请求来修复它,
关于websocket - Rouille 中 websocket 的高 CPU 使用率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56163680/