如何在多个Tonic gRPC服务间共享SeaORM数据库连接?
解决Tonic gRPC服务间共享SeaORM数据库连接的"value used here after move"错误
问题根源
DatabaseConnection类型遵循移动语义(未实现Copy trait):当你把conn传入第一个服务MyGreeter时,该变量的所有权已被转移,后续再尝试将同一个conn传给HealthCheck,就会触发"value used here after move"错误。
解决方案:用Arc包裹数据库连接
Arc(原子引用计数)是Rust标准库的线程安全智能指针,支持多线程环境下的共享所有权,完美适配Tonic gRPC服务的多线程运行场景。
步骤1:修改服务结构体,使用Arc<DatabaseConnection>
将服务中的connection字段类型替换为Arc<DatabaseConnection>:
use sea_orm::DatabaseConnection; use tonic::{Request, Response, Status}; use std::sync::Arc; pub mod hello_world { tonic::include_proto!("helloworld"); } pub use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; #[derive(Debug, Default)] pub struct MyGreeter { pub connection: Arc<DatabaseConnection>, // 改为Arc包裹的连接 } #[tonic::async_trait] impl Greeter for MyGreeter { async fn unary( &self, request: Request<HelloRequest>, ) -> Result<Response<HelloReply>, Status> { println!("Got a request: {:?}", request); // 使用连接时直接通过self.connection访问,Arc自动处理引用计数 // 示例执行查询:let users = User::find().all(&*self.connection).await?; let reply = hello_world::HelloReply { message: format!("Hello {}!", request.into_inner().name).into(), }; Ok(Response::new(reply)) } } // HealthCheck结构体做同样修改 #[derive(Debug, Default)] pub struct HealthCheck { pub connection: Arc<DatabaseConnection>, } // 实现Health服务逻辑...
步骤2:在main函数中用Arc包裹并克隆连接
用Arc::new包裹原始连接,再通过clone()创建共享引用(Arc的clone仅增加引用计数,不会复制整个连接,开销极小):
use std::sync::Arc; async fn main() -> Result<(), Box<dyn std::error::Error>> { let config = config::load(); let conn = connection::get(&config.database).await; let shared_conn = Arc::new(conn); // 用Arc包裹原始连接 let addr = format!("{}:{}", config.server.host, config.server.port).parse()?; Server::builder() .add_service(GreeterServer::new(MyGreeter { connection: Arc::clone(&shared_conn), // 克隆Arc引用 })) .add_service(HealthServer::new(HealthCheck { connection: Arc::clone(&shared_conn), // 再次克隆引用 })) .serve(addr) .await?; Ok(()) }
可行性说明
Arc提供线程安全的共享所有权,多个服务实例持有同一个连接的Arc引用,所有实例销毁后,引用计数归0,连接资源会自动释放。- SeaORM的
DatabaseConnection本身内置线程安全机制(内部用Arc和互斥锁实现),因此用Arc包裹后可安全在多线程gRPC服务间共享。
内容的提问来源于stack exchange,提问作者Nkk
相关产品推荐
相关产品推荐

