You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust Salvo框架多数据库连接池管理及并发阻塞问题咨询

问题描述

我正在用Rust的Salvo Web框架构建应用,需要支持多种数据库连接类型(无TLS的Postgres、带TLS的Postgres、无TLS的Redis、带TLS的Redis),配置由用户通过配置文件定义。每种连接类型可创建N个实例,每个实例有独立连接池,能通过名称引用。

初始实现代码

连接定义与池创建逻辑

use core::panic;
use std::collections::HashMap;

use bb8_postgres::PostgresConnectionManager;
use serde_json::Value;
use tokio_postgres::NoTls;

use bb8::Pool;

pub struct AnyConnection {
    name: String,
    details: HashMap<String, Value>,
    secure: bool
}

impl AnyConnection {
    pub fn new(name: String, details: HashMap<String, Value>, secure: bool) -> Self {
        AnyConnection { name, details, secure }
    }
}

#[salvo::async_trait]
pub trait PostgresPool {
    async fn make_unsecure(&self) -> Option<Pool<PostgresConnectionManager<NoTls>>>;
}

#[salvo::async_trait]
impl PostgresPool for AnyConnection {
    async fn make_unsecure(&self) -> Option<Pool<PostgresConnectionManager<NoTls>>> {
        let manager = PostgresConnectionManager::new_from_stringlike(
            &self.details.get("DATABASE_URL").unwrap().to_string(),
            tokio_postgres::NoTls,
        );

        let conn_manager = match manager {
            Ok(m) => m,
            Err(err) => panic!("Error opening a connection for: {} with error: {:?}", &self.name, err.to_string())
        };
        
        let pool = match Pool::builder().max_size(5).build(conn_manager).await {
            Ok(p) => p,
            Err(err) => panic!("Error creating pool for: {} with error: {:?}", &self.name, err.to_string())
        };

        Some(pool)
    }
}

初始化代码(main.rs)

let mut conns: HashMap<String, AnyConnection> = HashMap::new();

let mut details: HashMap<String, Value> = HashMap::new();
details.insert("DATABASE_URL".to_string(), Value::String("postgresql://localhost:5432/customers?user=postgres&password=postgres".to_string()));

let customers_db: AnyConnection = AnyConnection::new("customers_db".to_string(), details, false);        
let pg = PostgresPool::make_unsecure(&customers_db).await;

初始疑问

  1. 存储连接池对象引用的conns HashMap应该缓存到哪里?
  2. 连接是否会安全归还到该HashMap中存储的连接池?

更新内容与新问题

根据反馈改写了处理器代码:

#[async_trait]
impl Handler for HandlerWithPool {
    async fn handle(&self, req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
        let delay: Option<u64> = req.query::<u64>("delay");

        // self.db_pools is available here
        let db_pools = self.get_pools();
        let pgpool = db_pools.get("pg").unwrap();        

        let pgconn = match pgpool {
            AnyConn::Postgres(p) => {
                println!("Pool State: {:?}", p.state()); // 显示多个连接
                Some(p)
            },
            _ => None
        };

        let rows = pgconn.unwrap().get().await.unwrap().query("select * from users where id = $1", &[&"User-123"]).await.unwrap();        
        for row in rows {
            //println!("Row {:?}", row);
        }

        let dur = time::Duration::from_millis(delay.unwrap() * 1000);
        thread::sleep(dur);

        res.render("Hello World!");
    }
}

为测试连接池并发能力,添加了delay查询参数。发送第一个延迟10秒的请求后,第二个延迟1秒的请求需要等待第一个请求完成才执行,看起来连接池被锁定导致阻塞。另外说明:所有连接池均在服务器启动时创建,无需动态生成。


解答

一、连接池的缓存位置

在Salvo框架中,推荐两种全局缓存方式:

  • 全局单例:用once_cell::sync::Lazy或lazy_static定义全局的HashMap<String, AnyConn>枚举类型,包含不同数据库的连接池实例。因为连接池是启动时一次性创建的,全局单例能让所有处理器直接访问,逻辑简单高效。
  • Salvo全局Depot:在main函数中通过Service::new().depot()将连接池存入全局Depot,后续任意处理器都可通过&mut Depot取出使用,符合Salvo的请求上下文设计。

不建议仅在局部持有conns HashMap,这样无法实现跨处理器的连接池共享,不符合全局资源的使用逻辑。

二、连接的安全归还

bb8连接池会自动处理连接归还:当通过get().await获取的bb8::PooledConnection对象离开作用域被Drop时,bb8会自动将连接归还到池中,无需手动操作。只要你缓存的是全局唯一的Pool实例,连接就会安全归还到对应池内。

三、并发阻塞问题的原因与解决

你的代码阻塞的核心原因是使用了同步阻塞的thread::sleep(dur),它会占用当前Tokio工作线程,导致其他请求无法被调度处理。同时,这段代码会让获取到的数据库连接被持有10秒,快速耗尽连接池中的可用连接,加剧阻塞。

解决步骤:

  1. 替换同步睡眠为Tokio异步延迟:
use tokio::time::{sleep, Duration};

// 替换原来的thread::sleep
let dur = Duration::from_millis(delay.unwrap() * 1000);
sleep(dur).await;
  1. 确保连接池是全局唯一实例,避免重复创建独立池。
  2. 尽量缩短连接持有时间:如果延迟操作不需要数据库连接,可将sleep(dur).await放在数据库操作完成之后,让连接尽早归还到池中。

内容的提问来源于stack exchange,提问作者ed1t

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 11:38:10