是否可以在rust中将'await'与lazy_static!宏一起使用?

7rfyedvj  于 2023-01-13  发布在  其他
关注(0)|答案(4)|浏览(187)

我想在一个项目中使用异步MongoDB。
我不想传递客户端,因为它需要传递多个任务和线程,所以我使用lazy_static保留了一个静态客户端,但是,我不能在初始化块中使用await
我能做些什么来解决这个问题?
也欢迎提出不使用lazy_static的建议。

use std::env;
use futures::stream::StreamExt;
use mongodb::{
    bson::{doc, Bson},
    options::ClientOptions,
    Client,
};

lazy_static! {
    static ref MONGO: Option<Client> = {
        if let Ok(token) = env::var("MONGO_AUTH") {
            if let Ok(client_options) = ClientOptions::parse(&token).await
                                                                     ^^^^^
            {
                if let Ok(client) = Client::with_options(client_options) {
                    return Some(client);
                }
            }
        }
        return None;
    };
}
fcwjkofz

fcwjkofz1#

我是根据rust论坛上某个人的建议而采用这种方法的。

static MONGO: OnceCell<Client> = OnceCell::new();
static MONGO_INITIALIZED: OnceCell<tokio::sync::Mutex<bool>> = OnceCell::new();

pub async fn get_mongo() -> Option<&'static Client> {
    // this is racy, but that's OK: it's just a fast case
    let client_option = MONGO.get();
    if let Some(_) = client_option {
        return client_option;
    }
    // it hasn't been initialized yet, so let's grab the lock & try to
    // initialize it
    let initializing_mutex = MONGO_INITIALIZED.get_or_init(|| tokio::sync::Mutex::new(false));

    // this will wait if another task is currently initializing the client
    let mut initialized = initializing_mutex.lock().await;
    // if initialized is true, then someone else initialized it while we waited,
    // and we can just skip this part.
    if !*initialized {
        // no one else has initialized it yet, so

        if let Ok(token) = env::var("MONGO_AUTH") {
            if let Ok(client_options) = ClientOptions::parse(&token).await {
                if let Ok(client) = Client::with_options(client_options) {
                    if let Ok(_) = MONGO.set(client) {
                        *initialized = true;
                    }
                }
            }
        }
    }
    drop(initialized);
    MONGO.get()
}
rjee0c15

rjee0c152#

但是我不能在初始化块中使用await。
您可以使用futures::executor::block_on绕过此问题

use once_cell::sync::Lazy;
// ...
static PGCLIENT: Lazy<Client> = Lazy::new(|| {
    let client: Client = futures::executor::block_on(async {
        let (client, connection) = tokio_postgres::connect(
            "postgres:///?user=ecarroll&port=5432&host=/run/postgresql",
            NoTls,
        )
        .await
        .unwrap();

        tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("connection error: {}", e);
            }
        });

        client
    });
    client
});

我们遇到的是非异步闭包阻塞在单个线程中,直到将来解决为止。

oalqel3c

oalqel3c3#

tokio::runtime::Runtime创建一个新的运行时,并使用block_on阻塞当前线程,直到完成。

// database.rs
use tokio::runtime::Runtime;
use mongodb::Client;

pub fn connect_sync() -> Client {
    Runtime::new().unwrap().block_on(async {
        Client::with_uri_str("mongodb://localhost:27017").await.unwrap()
    })
}
// main.rs
mod database;

lazy_static! {
    static ref CLIENT: mongodb::Client =  database::connect_sync();
}

#[actix_web::main]
async fn main() {
   let collection = &CLIENT.database("db_name").collection("coll_name");
   // ...
}
gcxthw6b

gcxthw6b4#

使用async_once机箱。

use async_once::AsyncOnce;
use lazy_static::lazy_static;
use mongodb::Client;

lazy_static! {
    static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
        Client::with_uri_str(std::env::var("MONGO_URL").expect("MONGO_URL not set"))
            .await
            .unwrap()
    });
}

那么

CLIENT.get().await;

相关问题