【问题标题】:Spawn reading data from multipart in actix-web在 actix-web 中生成从多部分读取数据
【发布时间】:2021-05-16 01:12:12
【问题描述】:

我尝试了example of actix-multipartactix-web v3.3.2actix-multipart v0.3.0

举个简单的例子,

use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};

#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
    while let Ok(Some(mut field)) = payload.try_next().await {
        let content_type = field.content_disposition().unwrap();
        let filename = content_type.get_filename().unwrap();
        println!("filename = {}", filename);

        while let Some(chunk) = field.next().await {
            let data = chunk.unwrap();
            println!("Read a chunk.");
        }
        println!("Done");
    }
    HttpResponse::Ok().finish()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(save_file))
        .bind("0.0.0.0:8080")?
        .run()
        .await
}

这很好用,但我想异步处理表单数据。所以我尝试了:

use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};

#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
    actix_web::rt::spawn(async move {
        while let Ok(Some(mut field)) = payload.try_next().await {
            let content_type = field.content_disposition().unwrap();
            let filename = content_type.get_filename().unwrap();
            println!("filename = {}", filename);

            while let Some(chunk) = field.next().await {
                let data = chunk.unwrap();
                println!("Read a chunk.");
            }
            println!("Done");
        }
    });
    HttpResponse::Ok().finish()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(save_file))
        .bind("0.0.0.0:8080")?
        .run()
        .await
}

(已将actix_web::rt::spawn 添加到save_file。)

但这并没有起作用——消息"Done" 从未打印出来。第二种情况显示的"Read a chunk"的数量比第一种情况少,所以我猜field.next().await在完成读取所有数据之前由于某种原因无法终止。

我对异步编程了解不多,所以我不知道为什么field.next()actix_web::rt::spawn 中不起作用。

我的问题是:为什么会这样,我该如何处理 actix_web::rt::spawn

【问题讨论】:

  • 您的第一个代码已经是异步的。 OTOH,您的第二个代码在完成读取传入数据之前发送 OK 响应,这可能会提示发件人停止发送,假设他们尚未完成。
  • 对不起,我的意思是“返回一个响应,然后处理表单数据”。啊,明白了。所以我应该先获取数据,等待,然后返回响应,最后处理数据,对吧?

标签: asynchronous rust actix-web


【解决方案1】:

当您拨打此电话时:

actix_web::rt::spawn(async move {
    // do things...
});

spawn 返回一个用于轮询任务的JoinHandle。当您放下该句柄(不将其绑定到任何东西)时,该任务将“分离”,即它在后台运行。

actix 文档在这里并不是特别有用,但actix 在后台使用tokio 运行时。一个关键问题是在tokio 中,生成的任务不能保证完成。执行者需要以某种方式知道它应该在那个未来执行工作。在您的第二个示例中,生成的任务永远不会.awaited,也不会通过通道与任何其他任务进行通信。

很可能,生成的任务永远不会被轮询,也不会取得任何进展。为了确保它完成,您可以.awaitJoinHandle(这将推动任务完成)或.await其他一些Future,这取决于衍生任务中的工作(通常通过使用通道)。


至于您的更一般的目标,工作已经在异步执行!最有可能的是,actix 正在大致执行您在第二个示例中尝试执行的操作:收到请求后,它会生成一个任务来处理请求并反复轮询它(以及其他活动请求)直到它完成,然后发送响应。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-13
    • 2017-03-30
    • 2019-10-13
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多