actix 使用字符串传递应用程序状态

actix pass appliction state with string

您好,我想传递以下 AppState。

pub struct AppState {
    clients: Vec<Client>,
}

我的服务器是这样的:

async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
HttpServer::new(|| App::new()
    .data(Config::default().realm("Restricted area"))
    .data(AppState {
        clients: app_config.clients
    })
    .app_data(web::PayloadConfig::new(1000000))
    .service(web::resource("/").route(web::post().to(index))))
    .bind("127.0.0.1:8080")?
    .workers(1)
    .run()
    .await
}

我要传递的客户端实体结构:

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

我希望你清楚我想要完成的事情。 现在我的问题。 编译器说:

the trait bound entities::CmkClientServerConfig: std::clone::Clone is not satisfied in `[closure@src/server.rs:25:21: 31:66 app_config:entities::CmkClientServerConfig]

可能因为异步执行需要克隆这个东西。 因此将“#[derive(Clone)]”添加到 CmkClientServerConfig 结构和 Client 结构。

然后得到这个错误信息:

error[E0507]: cannot move out of `app_config.clients`, as `app_config` is a captured variable in an `Fn` closure
  --> src/server.rs:29:22
   |
23 | async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
   |                        ---------- captured outer variable
...
29 |             clients: app_config.clients
   |                      ^^^^^^^^^^^^^^^^^^ move occurs because `app_config.clients` has type `std::vec::Vec<entities::Client>`, which does not implement the `Copy` trait

error[E0507]: cannot move out of `app_config.clients`, as `app_config` is a captured variable in an `Fn` closure
  --> src/server.rs:29:22
   |
23 | async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
   |                        ---------- captured outer variable
...
29 |             clients: app_config.clients
   |                      ^^^^^^^^^^^^^^^^^^ move occurs because `app_config.clients` has type `std::vec::Vec<entities::Client>`, which does not implement the `Copy` trait

我的问题是如何正确地做到这一点,我怎样才能使克隆成为可能。

感谢您的帮助。

两者 API docs and the Get Started docs 解释:

HttpServer accepts an application factory rather than an application instance. An HttpServer constructs an application instance for each thread. Therefore, application data must be constructed multiple times. If you want to share data between different threads, a shareable object should be used, e.g. Send + Sync.

如果您的 clients 列表只在服务器启动时初始化,否则保持不可变,那么您可以在您的应用程序工厂函数中克隆它,但如果它假定是可变的,那么您必须将它包装在 Arc<Mutex<T>> 以便它可以安全地在 多个线程 运行 您的应用 .

之间进行克隆、共享和变异

如果 clients 列表未更改,则将您的客户端更新为 derive Clone 特征:

#[derive(Serialize, Deserialize, Debug, Clone)] // Clone added here
pub struct Client {
    pub id: String,
    pub password: String,
}

然后将您的应用程序工厂功能更新为 move 您的 app_config 本身,这样您就可以在 clients:

上重复调用 clone
async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
    HttpServer::new(move || App::new() // move added here
        .data(Config::default().realm("Restricted area"))
        .data(AppState {
            clients: app_config.clients.clone() // clone() added here
        })
        .app_data(web::PayloadConfig::new(1000000))
        .service(web::resource("/").route(web::post().to(index))))
        .bind("127.0.0.1:8080")?
        .workers(1)
        .run()
        .await
}

我认为您需要解决 app_config.clients 被移动的问题。为了让您在同一页面上,传递给 HttpServer::new 的 clojure 将在每个线程中执行一次。所以它会导致 app_config.clients 的多次移动。 (想一想你就会明白actix是如何工作的)。

也就是说,可以通过以下方式之一处理此问题,具体取决于哪些方式可行:

  1. 像这样为每个线程创建新的 Vec<Client>
...
.data(AppState {
        clients: create_new_clients()
    })
  1. 如果你的 Client 结构是 Clone 你也可以这样做:
...
.data(AppState {
        clients: app_config.clients.clone()
    })
  1. 所有其他方法都失败了,您可以像这样将客户包装在 Arc 内:
pub struct AppState {
    clients: Vec<Arc<Client>>,
}
...
.data(AppState {
        clients: app_config.clients.clone()
    })

Client 可能需要可变性,请检查建议 Arc<Mutex<Client>> 的其他答案