Rust 如何流式处理超请求';从一个产生数据块的处理速度慢的线程中删除主体?

Rust 如何流式处理超请求';从一个产生数据块的处理速度慢的线程中删除主体?,rust,rust-tokio,hyper,Rust,Rust Tokio,Hyper,我有一个程序生成数据很慢(我们可以说它是计算密集型的,就像计算圆周率的数字)。它产生了大量的数据;每个响应可以是1GB,不适合内存,并且必须根据需要生成。我正在使用hyper编写一个web服务,以便在请求时生成内容 让我们跳过样板文件(service\u fn,Server::bind) 缓慢生成数据的API可能类似于 use std::io; impl SlowData { fn new(initial: &str) -> SlowData { unim

我有一个程序生成数据很慢(我们可以说它是计算密集型的,就像计算圆周率的数字)。它产生了大量的数据;每个响应可以是1GB,不适合内存,并且必须根据需要生成。我正在使用hyper编写一个web服务,以便在请求时生成内容

让我们跳过样板文件(
service\u fn
Server::bind

缓慢生成数据的API可能类似于

use std::io;

impl SlowData {
    fn new(initial: &str) -> SlowData {
        unimplemented!()
    }

    fn next_block(&self) -> io::Result<&[u8]> {
        unimplemented!()
    }
}

type ResponseFuture = Box<Future<Item = Response, Error = GenericError> + Send>;

fn run(req: Request) -> ResponseFuture {
    // spawn a thread and:
    // initialize the generator
    // SlowData::new(&req.uri().path());

    // spawn a thread and call slow.next_block() until len()==0
    // each byte which comes from next_block should go to the client
    // as part of the Body
}
使用std::io;
impl慢数据{
fn新(首字母:&str)->慢数据{
未执行!()
}
fn下一个_块(&self)->io::结果{
未执行!()
}
}
类型响应未来=框;
fn运行(请求)->响应未来{
//生成一个线程并执行以下操作:
//初始化生成器
//SlowData::new(&req.uri().path());
//生成一个线程并调用slow.next_block(),直到len()=0
//来自下一个_块的每个字节都应该发送到客户端
//作为身体的一部分
}
请注意,
SlowData::new
也是计算密集型的

最理想的情况是,我们将最小化副本,并将该
和[u8]
直接发送到hyper,而无需将其复制到
Vec
或其他文件中


如何从侧线程实现超请求的主体?

在线程池中启动线程,并通过通道发送数据块。通道实现
,可以使用以下方法从
构建超
主体

use futures::{channel::mpsc,executor::ThreadPool,task::SpawnExt,SinkExt,Stream};//0.3.1,功能=[“线程池”]
使用超链接:{
服务:{make_service_fn,service_fn},
主体、响应、服务器、,
}; // 0.13.1
使用std::{convert::Infallible,io,thread,time::Duration};
使用tokio;//0.2.6,功能=[“宏”]
结构慢数据;
impl慢数据{
fn新(_首字母:&str)->SlowData{
线程::睡眠(持续时间::从秒(1));
自己
}
fn下一个_块(&self)->io::结果{
线程::睡眠(持续时间::从秒(1));
Ok(b“数据”)
}
}
fn流(池:线程池)->impl流{
let(mut-tx,rx)=mpsc::信道(10);
pool.spawn(异步移动{
设sd=SlowData::new(“虚拟”);
对于0..3中的uu{
设block=sd.next_block().map(|b | b.to_vec());
发送(块)。等待。期望(“无法发送块”);
}
})
.expect(“无法生成线程”);
接收
}
#[tokio::main]
异步fn main(){
//构造要侦听的SocketAddress。。。
让addr=([127,0,0,1],3000).into();
//创建线程池(克隆很便宜)。。。
让pool=ThreadPool::new().unwrap();
//处理每个连接。。。
设make_service=make_service_fn(| | u socket |{
让pool=pool.clone();
异步的{
//处理每个请求。。。
设svc_fn=service_fn(移动|请求|{
让pool=pool.clone();
异步的{
让数据=流(池);
让resp=Response::new(Body::wrap_流(数据));
结果:正常(resp)
}
});
结果:正常(svc_fn)
}
});
//捆绑和服务。。。
让server=server::bind(&addr).service(生成服务);
//最后,运行服务器
如果让Err(e)=server.wait{
eprintln!(“服务器错误:{}”,e);
}
}
创建线程时,无法避免将切片复制到
Vec

另见:

use futures::{channel::mpsc, executor::ThreadPool, task::SpawnExt, SinkExt, Stream}; // 0.3.1, features = ["thread-pool"]
use hyper::{
    service::{make_service_fn, service_fn},
    Body, Response, Server,
}; // 0.13.1
use std::{convert::Infallible, io, thread, time::Duration};
use tokio; // 0.2.6, features = ["macros"]

struct SlowData;
impl SlowData {
    fn new(_initial: &str) -> SlowData {
        thread::sleep(Duration::from_secs(1));
        Self
    }

    fn next_block(&self) -> io::Result<&[u8]> {
        thread::sleep(Duration::from_secs(1));
        Ok(b"data")
    }
}

fn stream(pool: ThreadPool) -> impl Stream<Item = io::Result<Vec<u8>>> {
    let (mut tx, rx) = mpsc::channel(10);

    pool.spawn(async move {
        let sd = SlowData::new("dummy");

        for _ in 0..3 {
            let block = sd.next_block().map(|b| b.to_vec());
            tx.send(block).await.expect("Unable to send block");
        }
    })
    .expect("Unable to spawn thread");

    rx
}

#[tokio::main]
async fn main() {
    // Construct our SocketAddr to listen on...
    let addr = ([127, 0, 0, 1], 3000).into();

    // Create a threadpool (cloning is cheap)...
    let pool = ThreadPool::new().unwrap();

    // Handle each connection...
    let make_service = make_service_fn(|_socket| {
        let pool = pool.clone();

        async {
            // Handle each request...
            let svc_fn = service_fn(move |_request| {
                let pool = pool.clone();

                async {
                    let data = stream(pool);
                    let resp = Response::new(Body::wrap_stream(data));

                    Result::<_, Infallible>::Ok(resp)
                }
            });

            Result::<_, Infallible>::Ok(svc_fn)
        }
    });

    // Bind and serve...
    let server = Server::bind(&addr).serve(make_service);

    // Finally, run the server
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}