结合bb8和postgres使用hyper的示例

时间:2019-07-17 13:25:04

标签: rust connection-pooling rust-tokio hyper

我想将hyperbb8tokio-postgres一起使用。在每个请求中,我都希望从池中获取一个新的连接。有人可以为我提供一些这种情况的例子吗? 目前我是这样的:

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();

    let pg_mgr =
        PostgresConnectionManager::new("postgresql://auth:auth@localhost:5433/auth", NoTls);

    rt::run(future::lazy(move || {
        Pool::builder()
            .build(pg_mgr)
            .map_err(|e| eprintln!("Database error: {}", e))
            .and_then(move |pool| {
                let service = || service_fn(|req| router(req, pool.clone()));

                let server = Server::bind(&addr)
                    .serve(service)
                    .map_err(|e| eprintln!("Server error: {}", e));

                println!("Listening on http://{}", addr);
                server
            })
    }))
}

fn router(
    _req: Request<Body>,
    _pool: Pool<PostgresConnectionManager<NoTls>>,
) -> Result<Response<Body>, hyper::Error> {
    // do some staff with pool
}

但是它不会编译:

error[E0597]: `pool` does not live long enough
  --> src/main.rs:22:63
   |
22 |                 let service = || service_fn(|req| router(req, pool.clone()));
   |                               -- -----------------------------^^^^----------
   |                               |  |                            |
   |                               |  |                            borrowed value does not live long enough
   |                               |  returning this value requires that `pool` is borrowed for `'static`
   |                               value captured here
...
30 |             })
   |             - `pool` dropped here while still borrowed

我在做什么错?如何使我的案件正常工作?

2 个答案:

答案 0 :(得分:2)

解决方案非常简单,但是要了解这个问题,我想提供一些其他信息...

  1. 当您将来调用and_then以获得结果时,它将变量的值传递到传递给and_then的闭包中,从而使您拥有该数据的所有权。

  2. hypers构建器上的方法serve(由Server::bind返回)期望闭包具有静态寿命。

现在要解决该问题:

  • 好:将封包的价值传递给服务,这会将其移动,从而转移所有权。
  • 好:service_fn是在and_then闭包之外定义的,因此该函数的寿命足够长了
  • 错误:闭包使用局部变量池将其传递给service_fn

要解决此问题,只需将move本地数据放入闭包中,如下所示:

let service = move || service_fn(|req| router(req, pool));

答案 1 :(得分:0)

找到解决办法here

最简单的解决方案如下:

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();

    let pg_mgr =
        PostgresConnectionManager::new("postgresql://auth:auth@localhost:5433/auth", NoTls);

    rt::run(future::lazy(move || {
        Pool::builder()
            .build(pg_mgr)
            .map_err(|_| eprintln!("kek"))
            .and_then(move |pool| {
                let service = move || {
                    let pool = pool.clone();
                    service_fn(move |req| router(req, &pool))
                };

                let server = Server::bind(&addr)
                    .serve(service)
                    .map_err(|e| eprintln!("Server error: {}", e));

                println!("Listening on http://{}", addr);
                server
            })
    }))
}

fn router(
    _req: Request<Body>,
    _pool: &Pool<PostgresConnectionManager<NoTls>>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error> {
    // some staff
}

还可以使用servicert::runArc之外构造Mutex

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();

    let pg_mgr =
        PostgresConnectionManager::new("postgresql://auth:auth@localhost:5433/auth", NoTls);
    let pool: Arc<Mutex<Option<Pool<PostgresConnectionManager<NoTls>>>>> =
        Arc::new(Mutex::new(None));
    let pool2 = pool.clone();

    let service = move || {
        let pool = pool.clone();
        service_fn(move |req| {
            let locked = pool.lock().unwrap();
            let pool = locked
                .as_ref()
                .expect("bb8 should be initialized before hyper");
            router(req, pool)
        })
    };

    rt::run(future::lazy(move || {
        Pool::builder()
            .build(pg_mgr)
            .map_err(|_| eprintln!("kek"))
            .and_then(move |pool| {
                *pool2.lock().unwrap() = Some(pool);

                let server = Server::bind(&addr)
                    .serve(service)
                    .map_err(|e| eprintln!("Server error: {}", e));

                println!("Listening on http://{}", addr);
                server
            })
    }))
}

fn router(
    _req: Request<Body>,
    _pool: &Pool<PostgresConnectionManager<NoTls>>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error> {
    // some staff
}