【问题标题】:Rust lazy_static with async/await?使用 async/await 生锈 lazy_static?
【发布时间】:2021-05-22 14:52:20
【问题描述】:

我是 Rust 的新手。我正在使用带有异步运行时(tokio)的 mongodb。

我想全局初始化mongo客户端,所以我使用了一个名为lazy_static的板条箱。问题是mongodb异步连接,现在lazy_static不支持async关键字.. .

use mongodb::Client;
async {
   let client = Client::with_uri_str(&env_var("MONGO_URL")).await.unwrap();
}

那么如何全局初始化client对象呢?

相关:

Async/await support?
alternative to using 'await' with lazy_static! macro in rust?

【问题讨论】:

  • 如果使用 tokio 提供的 block_on 函数而不是 awaiting 来阻止初始化是可以的,那么结果将完成工作。
  • 您链接的 StackOverflow Q&A 有两个建议:block_on 或使用 OnceCell。为什么那里的答案不充分?
  • 据我了解,您正在寻找的是类似于lazy_static!OnceCell 包装器,除了它使用async 块而不是闭包进行初始化。但是,在这种情况下,mongodb 库似乎编码不佳,with_uri_str 方法实际上并不需要为async,所以我只使用block_on
  • 感谢大家的宝贵时间,但使用您的方法会产生新的问题,我仍在寻找答案,至少 Minimal, Reproducible Example
  • @Coder-256、@Aiden4、@kmdreko,我试过OnceCell futures::executor::block_ontokio::runtime::Runtime::new等,它们都会产生新的问题,比如“Cannot start a runtime from within a runtime”或“thread 'tokio- runtime-worker' 惊慌失措,因为“无法从另一个执行程序中执行 LocalPool 执行程序:EnterError”等等......

标签: asynchronous rust


【解决方案1】:

如果您使用新的运行时并且惰性静态首先在现有运行时的上下文中使用,如下例所示:

use lazy_static::lazy_static;
use mongodb::Client;

lazy_static! {
    static ref CLIENT: Client = {
        tokio::runtime::Runtime::new().unwrap().block_on(async {
            let uri = std::env::var("MONGO_URL").unwrap();
            let client = Client::with_uri_str(&uri).await.unwrap();

            client
        })
    };
}

#[tokio::main]
async fn main() {
    let _db = CLIENT.database("local");
}

你会得到提到的错误:

thread 'main' panicked at 'Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.', C:\Users\kmdreko\.cargo\registry\src\github.com-1ecc6299db9ec823\tokio-1.6.1\src\runtime\enter.rs:39:9

也许可以通过使用不同的运行时规避此问题(futures vs tokio vs async-std)但这并不理想,因为它仍然会阻塞底层运行时。


解决这个问题的相对直接的方法是尝试懒惰地做它并立即在main中初始化它。这样您就可以直接使用异步运行时,而不必担心在其他地方需要它:

use mongodb::Client;
use once_cell::sync::OnceCell;

static CLIENT: OnceCell<Client> = OnceCell::new();

#[tokio::main]
async fn main() {
    let uri = std::env::var("MONGO_URL").unwrap();
    let client = Client::with_uri_str(&uri).await.unwrap();
    CLIENT.set(client).unwrap();

    let _db = CLIENT.get().unwrap().database("local");
}

这可以通过OnceCell(如上所示)或类似RwLock(如有必要)来完成。


对于您要实现的目标,最直接的答案是使用 async_once crate,这使得它使用接收器的运行时来驱动异步函数。

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

lazy_static! {
    static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
        let uri = std::env::var("MONGO_URL").unwrap();
        let client = Client::with_uri_str(&uri).await.unwrap();

        client
    });
}

#[tokio::main]
async fn main() {
    let _db = CLIENT.get().await.database("local");
}

这假设客户端的所有或几乎所有使用都将在异步上下文中。

【讨论】:

    猜你喜欢
    • 2022-12-13
    • 2017-08-21
    • 2018-06-04
    • 2018-08-22
    • 2020-03-25
    • 1970-01-01
    • 2021-06-05
    • 2017-08-23
    • 2021-05-28
    相关资源
    最近更新 更多