如何让Axum Handler与Mutex保护的缓存正常工作?
解决Axum中使用Mutex保护LruCache的Handler trait错误
错误原因
你的代码触发编译错误的核心问题是持有std::sync::Mutex锁的同时调用了.await:
std::sync::Mutex的锁守卫(MutexGuard)并非Send类型,而Axum的Handlertrait要求处理函数返回的Future必须是Send(因为Axum基于Tokio多线程Runtime调度任务)。- 当你在持有锁的状态下执行
reqwest::get(...).await时,Future会捕获MutexGuard,导致整个Future失去Send属性,无法满足Handler的约束条件,最终触发E0277错误。
解决方案
有两种常用的修复方式,按需选择:
方式一:使用Tokio异步Mutex(推荐)
Tokio提供的tokio::sync::Mutex是为异步场景设计的,它的锁守卫支持Send,且.lock()方法返回Future,适配异步调度逻辑。
修改步骤:
- 替换Mutex的导入:
use tokio::sync::{Arc, Mutex}; // 或单独导入 tokio::sync::Mutex
- 保持
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
相关产品推荐
相关产品推荐

