Ржавая нить actix-web небезопасное движение

Я пытаюсь написать конечную точку HTTP с помощью actix-web 1.0. Я уменьшил функцию так, чтобы она просто возвращала переданного ей пользователя, но компилятор по-прежнему выдает ошибку.

extern crate actix_web;
extern crate chrono;
extern crate futures;
extern crate listenfd;
#[macro_use]
extern crate serde_derive;
extern crate dotenv;
use actix_web::{error, web, App, Error, HttpResponse, HttpServer};
use futures::future::Future;

#[derive(Debug, Deserialize, Serialize)]
pub struct LoginUser {
    pub username: String,
    pub password: String,
}

pub fn login(
    login_user: web::Json<LoginUser>,
) -> impl Future<Item = HttpResponse, Error = error::BlockingError<Error>> {
    web::block(move || {
        let login_user = login_user.into_inner();
        let user = LoginUser {
            username: login_user.username,
            password: login_user.password,
        };
        Ok(HttpResponse::Ok().json(user))
    })
}

pub fn router(cfg: &mut web::ServiceConfig) {
    cfg.service(web::scope("/").service(web::resource("").route(web::get().to(login))));
}

fn main() -> std::io::Result<()> {
    HttpServer::new(move || App::new().configure(router))
        .bind("127.0.0.1:3000")?
        .run()
}

Вот мой груз.томл.

[package]
name = "log"
version = "0.1.0"
authors = ["[email protected]"
edition = "2018"

[dependencies]
actix-files = "~0.1"
actix-web = "~1.0"
chrono = { version = "0.4.6", features = ["serde"] }
listenfd = "0.3"
diesel = {version = "1.4.1", features = ["postgres", "uuid", "r2d2", "chrono"]}
dotenv = "0.13"
failure = "0.1"
futures = "0.1"
scrypt = "0.2.0"
serde_derive="1.0"
serde_json="1.0"
serde="1.0"

Я получаю ошибку компиляции

|     web::block(move || {
|     ^^^^^^^^^^ `(dyn std::any::Any + 'static)` cannot be sent between threads safely

Я думаю, это как-то связано с использованием login_user в web::block, но по ошибке это трудно отличить. Каков предпочтительный способ безопасного асинхронного использования параметров запроса в Rust или actix?


person CallMeNorm    schedule 19.06.2019    source источник
comment
Круто, спущу на небольшую площадку.   -  person CallMeNorm    schedule 19.06.2019
comment
Возможно, что на игровой площадке нет нужных ящиков (почти уверен, что это верно для Actix), в этом случае вам следует применить те же методы, чтобы создать MCVE локально, а затем опубликовать свой main.rs файл. Спасибо!   -  person Shepmaster    schedule 19.06.2019


Ответы (1)


Ну, во-первых, HttpResponse не реализует Send. Поскольку web::block() запускает закрытие в пуле потоков, это проблема. Таким образом, вам нужно вернуть значение, которое равно Send из web::block, а затем создать из этого HttpResponse - например, используя and_then().

Во-вторых, в вашем маршрутизаторе вы используете web::get().to(login). Если вы хотите вызвать функцию, возвращающую Future, это должно быть web::get().to_async(login).

В-третьих, закрытие в web::block должно вернуть Result. Поскольку вы никогда не возвращаете значение ошибки, компилятор не может определить тип ошибки. Вам нужно дать компилятору подсказку. Обычно подойдет std::io::Error, поэтому верните Ok::<_, std::io::Error>(...value...).

В-четвертых, web::block возвращает BlockingError<E>. Вы можете использовать from_err(), чтобы сопоставить это с чем-то, что вы можете вернуть.

Итак, со всем этим соответствующая часть вашего кода будет выглядеть так:

pub fn login(
    login_user: web::Json<LoginUser>,
) -> impl Future<Item = HttpResponse, Error = Error> {
    web::block(move || {
        let login_user = login_user.into_inner();
        let user = LoginUser {
            username: login_user.username,
            password: login_user.password,
        };
        Ok::<_, std::io::Error>(user)
    })
        .from_err()
        .and_then(|user| HttpResponse::Ok().json(user))
}

pub fn router(cfg: &mut web::ServiceConfig) {
    cfg.service(web::scope("/").service(web::resource("").route(web::get().to_async(login))));
}
person miquels    schedule 25.06.2019
comment
Это произошло и со мной, но CallMeNorm сказал, что я уменьшил функцию, так что она просто возвращает пользователя, который ей передан. Итак, я предполагаю, что в исходном коде есть какой-то фактический код блокировки. - person miquels; 25.06.2019
comment
@miquels спасибо за ваш ответ. Как вы от ошибки добрались до своего ответа. Частично проблема для меня заключалась в том, что ошибка «std :: any :: Any» не может быть отправлена ​​между потоками, не оставляла мне много места для поиска. - person CallMeNorm; 25.06.2019
comment
О да. Компилятор ржавчины обычно очень помогает с сообщениями об ошибках, но в некоторых случаях - особенно с фьючерсами - они могут быть довольно запутанными. Из предыдущего опыта я знаю, что actix-web имеет немного другой дизайн, чем обычный tokio. Он распределяет нагрузку между запросами по нескольким потокам, но затем запрос выполняется в одном потоке, которому он был назначен, а несколько типов actix не являются Send. Они не должны быть такими. - person miquels; 26.06.2019