简体   繁体   English

Rust 多线程异步 Websocket 服务器

[英]Rust Multithread Asynchronous Websocket Server

I wanted to learn Rust and so I decided to use it for a real-world project.我想学习 Rust,所以我决定将它用于实际项目。

The idea is to have a server that这个想法是有一个服务器

  1. from the main thread A spawns a new thread B that performs some async task that produces a stream of values through time从主线程A产生一个新线程B ,它执行一些异步任务,通过时间产生 stream 的值

  2. receives client websocket connections [c, d, e, ..] asynchronously and handles them concurrently spawning new threads [C, D, E, ...]异步接收客户端 websocket 连接[c, d, e, ..]并同时处理它们产生新线程[C, D, E, ...]

  3. sends the values produced in thread B to threads [C, D, E, ...]将线程 B 中产生的值发送到线程[C, D, E, ...]

  4. each thread in [C, D, E, ...] publishes the value to their respective client in [c, d, e, ..] [C, D, E, ...]中的每个线程将值发布到[c, d, e, ..]中各自的客户端

I am using我在用

  • tokio to spawn new threads and tokio::sync::mpsc::unbounded_channel to send the values computed in B to the other threads tokio产生新线程和tokio::sync::mpsc::unbounded_channelB中计算的值发送到其他线程

  • tokio_tungstenite to manage websocket connections and send values to the clients tokio_tungstenite管理 websocket 连接并向客户端发送值

I managed to get a working example where thread B produces integers and fixed time intervals.我设法得到一个工作示例,其中线程B生成整数和固定时间间隔。 When the server starts, B starts producing a stream of values [0,1,2,3, ..] .服务器启动时, B开始生成值[0,1,2,3, ..]的 stream。

When a new websocket connection is opened, the client will receive the stream of data, starting from the value produced after the connection is opened (so that if the connection starts after the value 3 is produced by B , then the client will receive values from 4 onward).当一个新的 websocket 连接打开时,客户端将收到 stream 的数据,从连接打开后产生的值开始(这样如果连接在B产生值3之后开始,那么客户端将从4起)。

Here is the catch.这是陷阱。

The only way I found to for the receiving part of the channel in C to receive values asynchronously (and therefore prevent it from buffering the values and sending them to c just when B is completely done) is to use a loop that I believe consumes 100% of CPU.我发现C中通道的接收部分异步接收值的唯一方法(并因此防止它缓冲值并在B完全完成时将它们发送到c )是使用我认为消耗 100 的循环CPU 百分比。

I noted that because of this, every websocket connection will consume 100% of CPU (so if there are two connections open CPU usage will be 200% and so on).我注意到,因此,每个 websocket 连接将消耗 100% 的 CPU(因此,如果有两个连接打开,CPU 使用率将是 200%,依此类推)。

Here is the loop:这是循环:

loop {
    while let Ok(v) = rx.try_recv() {
       println!("PRINTER ID [{}] | RECEIVED: {:#?}", addr, v);
       println!("PRINTER ID [{}] | SENDING TO WS: {:#?}", addr, v);
       let mess = Message::Text(v.to_string());ws_sender.send(mess).await?;
}

If I use recv() (instead of try_recv() ) the values will be buffered and released to the websocket just when B is done.如果我使用recv() (而不是try_recv() ),这些值将被缓冲并在B完成时释放到 websocket。

I tried to use futures_channel::unbounded instead of the tokio channel but I have the same buffer problem.我尝试使用futures_channel::unbounded而不是tokio通道,但我遇到了同样的缓冲区问题。

QUESTION: how to rewrite the above loop to avoid using 100% and stream values to websocket without blocking?问题:如何重写上述循环以避免在不阻塞的情况下将 100% 和 stream 值用于 websocket?

You can see the tokio server here: https://github.com/ceikit/async_data/blob/master/src/bin/tokio_server.rs你可以在这里看到 tokio 服务器: https://github.com/ceikit/async_data/blob/master/src/bin/tokio_server.rs

you can test it by spinning a websocket connection in another terminal window running client您可以通过在另一个运行客户端的终端 window 中旋转 websocket 连接来测试它

needed to change thread::sleep to use futures-timer and sync::Mutex to futures::lock::Mutex , then a while-let with recv() works perfectly需要更改thread::sleep以使用futures-timersync::Mutexfutures::lock::Mutex ,然后使用recv()while-let可以完美运行

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM