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

如何让Axum Handler与Mutex保护的缓存正常工作?

解决Axum中使用Mutex保护LruCache的Handler trait错误

错误原因

你的代码触发编译错误的核心问题是持有std::sync::Mutex锁的同时调用了.await:

  • std::sync::Mutex的锁守卫(MutexGuard)并非Send类型,而Axum的Handler trait要求处理函数返回的Future必须是Send(因为Axum基于Tokio多线程Runtime调度任务)。
  • 当你在持有锁的状态下执行reqwest::get(...).await时,Future会捕获MutexGuard,导致整个Future失去Send属性,无法满足Handler的约束条件,最终触发E0277错误。

解决方案

有两种常用的修复方式,按需选择:

方式一:使用Tokio异步Mutex(推荐)

Tokio提供的tokio::sync::Mutex是为异步场景设计的,它的锁守卫支持Send,且.lock()方法返回Future,适配异步调度逻辑。

修改步骤:

  1. 替换Mutex的导入:
use tokio::sync::{Arc, Mutex}; // 或单独导入 tokio::sync::Mutex
  1. 保持Cache类型定义不变,修改锁的获取逻辑:
async fn generate(Extension(cache): Extension<Cache>) -> impl IntoResponse {
    let key: u64 = ...;

    // 异步获取锁,替换原有的 cache.lock().unwrap()
    let mut g = cache.lock().await;
    let data = match g.get(&key) {
        Some(v) => {
            println!("Match cache {}", key);
            v.to_owned()
        }
        None => {
            println!("Retrieve url");
            match reqwest::get("https://www.rust-lang.org/").await {
                Ok(response) => match response.bytes().await {
                    Ok(body) => {
                        g.put(key, body.clone());
                        body
                    }
                    Err(_) => Bytes::new(),
                },
                Err(_) => Bytes::new(),
            }
        }
    };

    ...
}

方式二:将IO操作移出锁范围

如果坚持使用std::sync::Mutex,可以拆分锁的持有逻辑:先获取锁检查缓存,无命中则释放锁执行IO,完成后再重新获取锁存入缓存。这种方式避免锁持有时间跨.await点,但可能存在多请求重复触发IO的竞争问题。

修改后的函数代码:

async fn generate(Extension(cache): Extension<Cache>) -> impl IntoResponse {
    let key: u64 = ...;

    // 第一步:获取锁检查缓存,完成后立即释放锁
    let cached_data = {
        let guard = cache.lock().unwrap();
        guard.get(&key).cloned()
    };

    let data = match cached_data {
        Some(v) => {
            println!("Match cache {}", key);
            v
        }
        None => {
            println!("Retrieve url");
            // 释放锁后执行异步IO请求
            let body = match reqwest::get("https://www.rust-lang.org/").await {
                Ok(response) => response.bytes().await.unwrap_or_default(),
                Err(_) => Bytes::new(),
            };

            // 第二步:重新获取锁,将结果存入缓存
            if !body.is_empty() {
                let mut guard = cache.lock().unwrap();
                guard.put(key, body.clone());
            }

            body
        }
    };

    ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 16:15:11