Как обновить Actix app_data с помощью POST-запроса?

Я пытаюсь создать сервер Actix, который хочу использовать в качестве интерфейса для глобального HashMap.

Мне удалось создать маршрут, который возвращает всю структуру. Однако теперь у меня возникают проблемы с обновлением HashMap. Я могу отправлять и получать запросы POST, однако после попытки использовать маршрут публикации, за которым следует маршрут get_topics, результирующая HashMap пуста {}.

// Publish to topic (creating new topic if none of the same name exists, else update the data)
async fn publish_topic(
    topics: web::Data<Mutex<Map<String, Value>>>,
    body: web::Bytes,
) -> Result<HttpResponse, Error> {
    let result = serde_json::from_str(std::str::from_utf8(&body).unwrap());
    let publish_req: Info = match result {
        Ok(v) => v,
        Err(e) => Info {
            topic: "_".to_string(),
            data: json!(e.to_string()),
        },
    };
    println!("[ SERVER ]: POST Req: {:?}", publish_req);
    topics
        .lock()
        .unwrap()
        .insert(publish_req.topic, publish_req.data);
    let map = topics.lock().unwrap().clone();
    println!("\n\n{:?}\n\n", topics.lock().unwrap());
    let body = serde_json::to_string_pretty(&map).unwrap();
    return Ok(HttpResponse::Ok().json(body));
}

#[actix_web::main]
pub async fn start_server() {
    std::env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();

    let (tx, _) = mpsc::channel();

    thread::spawn(move || {
        let sys = System::new("http-server");

        let srv = HttpServer::new(move || {
            let topics: web::Data<Mutex<Map<String, Value>>> =
                web::Data::new(Mutex::new(Map::new()));

            App::new()
                .app_data(topics.clone()) // add shared state
                // enable logger
                .wrap(middleware::Logger::default())
                // register simple handler
                .service(web::resource("/").route(web::get().to(get_topics)))
                .service(web::resource("/publish").route(web::post().to(publish_topic)))
        })
        .bind("127.0.0.1:8080")?
        .run();

        let _ = tx.send(srv);
        sys.run()
    });
}

person chris    schedule 29.05.2021    source источник


Ответы (1)


Что ж, ваша проблема проистекает из вашего подхода. Давайте посмотрим на строку ниже:

let srv = HttpServer::new(move || {
      let topics: web::Data<Mutex<Map<String, Value>>> =
          web::Data::new(Mutex::new(Map::new()));
          ...

Из-за того, как actix-web использует фабрику приложений, чтобы создать новый http-сервер, это говорит ему создать новую карту тем в каждом потоке. Я полагаю, это не то, что вы пытаетесь сделать сами. Чтобы решить вашу проблему, вам нужно будет создать экземпляр тем и клонировать его для всех запущенных потоков. Это будет работать:

let topics: web::Data<Mutex<Map<String, Value>>> =
            web::Data::new(Mutex::new(Map::new()));

HttpServer::new(move || {
    App::new()
        .app_data(topics.clone())

Этот ответ также может быть хорошей справкой.

person Njuguna Mureithi    schedule 31.05.2021