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

Axum框架共享状态因生命周期问题失效的技术咨询

解决Axum App State中的生命周期与所有权问题

错误原因分析

你遇到的两个错误本质是同一个问题:

  • Axum的.with_state()要求传入的状态必须满足'static生命周期(服务器会持续运行,状态需要存活到服务器停止),但你用&mut String作为引用,它的生命周期仅局限于main函数中name变量的存活期,服务器启动后name会被销毁,导致引用悬空。
  • 另外,Arc用于共享所有权,但你在其中存放了持有可变引用的结构体——可变引用本身只能有一个所有者,这和Arc的共享语义完全矛盾,根本无法在多线程的Axum服务器中安全使用。

解决方案

要实现可复用的App State,核心是让状态满足Send + Sync + 'static,同时安全共享可变数据(如果需要的话)。针对你的测试场景和实际数据库连接需求,分两种情况处理:

1. 测试场景:共享可变字符串

把可变字符串放到线程安全的容器中,用Arc<Mutex<T>>实现多线程下的安全共享与修改:

#![allow(incomplete_features)]
#![feature(async_fn_in_trait)]

use axum::{routing::post, Router};
use std::{net::SocketAddr, sync::{Arc, Mutex}};

mod modules;
use modules::auth::handlers::login;

// 移除不必要的生命周期,让AppState满足'static要求
pub struct AppState {
    user_credentials_repository: UserCredentialsRepository,
}

pub struct UserCredentialsRepository {
    // 用Arc<Mutex>包裹可变字符串,实现线程安全的共享
    shared_name: Arc<Mutex<String>>,
}

impl UserCredentialsRepository {
    pub fn new(shared_name: Arc<Mutex<String>>) -> Self {
        Self { shared_name }
    }

    // 示例:修改字符串的方法
    pub async fn update_name(&self, new_name: String) {
        let mut name = self.shared_name.lock().unwrap();
        *name = new_name;
    }
}

#[tokio::main]
async fn main() {
    let name = Arc::new(Mutex::new(String::from("Tom")));
    let user_credentials_repository = UserCredentialsRepository::new(name.clone());

    let shared_state = Arc::new(AppState {
        user_credentials_repository,
    });

    let app = Router::new()
        .route("/login", post(login))
        .with_state(shared_state);

    let addr = SocketAddr::from(([127, 0, 0, 1], 7777));

    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

2. 实际需求:复用数据库连接/会话

大部分数据库客户端(比如sqlx、Diesel的连接池)本身就是线程安全的,不需要额外的Mutex,直接用Arc包裹放到AppState即可。以sqlx的PostgreSQL连接池为例:

use axum::{routing::post, Router};
use sqlx::PgPool;
use std::{net::SocketAddr, sync::Arc};

pub struct AppState {
    db_pool: Arc<PgPool>,
}

#[tokio::main]
async fn main() {
    // 初始化数据库连接池
    let db_pool = PgPool::connect("postgres://user:pass@localhost/db").await.unwrap();
    let shared_state = Arc::new(AppState {
        db_pool: Arc::new(db_pool),
    });

    let app = Router::new()
        .route("/login", post(login))
        .with_state(shared_state);

    let addr = SocketAddr::from(([127, 0, 0, 1], 7777));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

处理器中使用状态的示例:

pub async fn login(State(state): State<Arc<AppState>>) -> impl IntoResponse {
    // 从连接池获取连接
    let conn = state.db_pool.acquire().await.unwrap();
    // 执行数据库操作...
    "Login success"
}

关键要点

  • Axum的State必须满足Send + Sync + 'static,所以不要在状态中持有短生命周期的引用,尽量持有所有权或用Arc包裹线程安全的类型。
  • 如果需要共享可变数据,用Arc<Mutex<T>>(独占访问)或Arc<RwLock<T>>(读写分离),确保多线程下的安全。
  • 数据库连接池本身已经是线程安全的,直接用Arc包裹即可,不需要额外的同步容器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:12:18