Rouille中websocket的高CPU使用率

Rouille中websocket的高CPU使用率,websocket,rust,Websocket,Rust,我有一个用Rust编写的小型web应用服务器,代码是 它所有功能都正常,但在“一段时间”内积极使用后,进程CPU使用率将达到100%(如top所报告),并一直保持到重新启动为止 问题是我无法找出导致CPU使用的原因,也无法有效地进行调试——主要是因为我无法在本地重新创建CPU使用情况。它发生在FreeBSD(进程被直接访问)和运行Ubuntu18.10的新服务器(通过Nginx反向代理)上;但是,我无法在运行macOS的本地计算机上重新创建此功能(可能是由于操作系统的原因,但可能只是不同的访问权

我有一个用Rust编写的小型web应用服务器,代码是

它所有功能都正常,但在“一段时间”内积极使用后,进程CPU使用率将达到100%(如
top
所报告),并一直保持到重新启动为止

问题是我无法找出导致CPU使用的原因,也无法有效地进行调试——主要是因为我无法在本地重新创建CPU使用情况。它发生在FreeBSD(进程被直接访问)和运行Ubuntu18.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

然而,这一切似乎都与Rouille的例子相符——这是Rouille(或底层的tiny_http库)中的一个bug,还是有什么明显的错误?

正如@摇头丸M0rse在评论中发现的,问题在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...]
fn下一步(&mut self)->选项{
环路{
[…剪断…]
设n=匹配self.socket.as_mut().unwrap().read(&mut buf){
Ok(n)=>n,
Err(ref Err)如果Err.kind()==io::ErrorKind::Interrupted=>0,
错误(41;)=>{
self.socket=无;
不返回任何值;
},
};
[…剪断…]
对于self.state\u machine.feed(&buf[0..n])中的元素{
[…剪断…]
调用
read(…)
可能会返回
Ok(0)
,表示套接字关闭时出现EOF(不是通过websocket协议关闭,而是在网络级别,例如设备失去连接)

由于
n
被设置为零,元素的
由于
0..n
范围而在循环中,然后再次调用
read(…)
,然后循环回调用
read()
(它再次返回
Ok(0)
,并且永远循环)

这个bug的复制是使用websocket客户端,比如连接到服务器,并使用sigint(ctrl+c)杀死客户端几次

我已经在Rouille问题跟踪器上报告了这个bug,并创建了一个pull请求来修复它


我快速查看了
rouille
的源代码,似乎如果
read
返回
Ok(0)
可能会导致
websocket.next()
无限循环。
Ok(0)
可以返回以指示EOF。您能在这里设置一个断点并尝试确认吗?@摇头丸M0rse-Aha,发现得很好-这确实是问题所在!如果套接字突然关闭,则循环将被卡住,
n
设置为零
(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
},
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...]