Rust Rocket通过State访问r2d2连接池出现线程安全错误问题
错误原因
你存入Rocket Managed State的DBPool结构体中存储的是PooledConnection(单个数据库连接实例),而Diesel的数据库连接内部使用了Cell、RefCell这类不满足Sync约束的内部可变性结构,本身无法跨多线程安全共享。但Rocket的State要求所有托管状态必须实现Send + Sync + 'static约束(需要在多个请求处理线程间共享访问),所以触发了线程安全校验错误。
修复方案
你需要把整个r2d2连接池实例存入State,而不是单个连接:r2d2的Pool类型本身已经实现了Send + Sync,可以安全跨线程共享,请求处理时再从池中动态获取可用连接即可。
修正后的代码
// 调整establish_pooled_connection的返回值,直接返回连接池实例 pub async fn establish_pooled_connection() -> Pool<ConnectionManager<PgConnection>> { dotenv().ok(); let database_url = env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); let manager = ConnectionManager::<PgConnection>::new(&database_url); r2d2::Pool::builder().build(manager).expect("Failed to create pool.") } // DBPool存储整个连接池,而不是单个连接 struct DBPool{ db_pool: Pool<ConnectionManager<PgConnection>> } #[rocket::main] async fn main() { let pool = establish_pooled_connection(); rocket::build() .mount("/",routes![callapi]) .manage(DBPool{db_pool: pool}) .launch() .await.ok(); } #[post("/callapi", data = "<request>")] async fn callapi(request: RequestAPI<'_>, dbpool: &State<DBPool>) -> Json<models::api_response::ApiResponse> { // 需要用连接时从池中获取 let conn = dbpool.db_pool.get().expect("Failed to get db connection from pool"); // 后续用conn执行数据库操作即可 // ... 你的原有逻辑 }
补充注意点
- 你原有代码中
establish_pooled_connection返回的是Result,你在main中直接使用时没有处理错误,修正后的代码直接在连接池创建失败时panic,你可以根据自己的需求调整错误处理逻辑。 - Diesel 1.4.x本身是同步实现,在异步Rocket路由中调用Diesel的数据库操作时,建议用
tokio::task::spawn_blocking包裹阻塞的数据库操作,避免阻塞异步runtime线程。
内容的提问来源于stack exchange,提问作者Gonzalo
相关产品推荐
相关产品推荐

