示例说明
moka 是并发友好的同步缓存:分段锁设计让高频读写几乎无争用。TTL 与最大容量组合成完整的生命周期策略——条目要么到期要么被权重淘汰;weigher 按条目实际占用赋权,重量级值会更快触发淘汰。try_get_with 的原子加载保证并发未命中时只有一个任务真正执行加载器,其余等待同一结果。
技术要点
- TTL 与容量双重生命周期约束
- weigher 按实际占用赋权淘汰
- try_get_with 原子防击穿加载
代码解读
build_cache 组合三十秒 TTL、一万容量与按费率条数计权的 weigher;手动模式演示先查后填的经典旁路缓存;自动模式用 try_get_with 把键与加载 future 一起传入——未命中时原子执行,两次演示互不重复。entry_count 与 weighted_size 输出运行期指标。
使用建议
多实例部署时进程内缓存口径独立,强一致需求外置 Redis。加载器失败会让 try_get_with 返回错误,调用方要有降级路径。TTL 按数据可容忍陈旧度设定。
代码示例
use moka::future::Cache;
use std::time::Duration;
#[derive(Debug, Clone)]
struct ExchangeRates {
base: String,
rates: Vec<(String, f64)>,
}
async fn fetch_rates(base: &str) -> ExchangeRates {
// 模拟外部 API 的网络往返
tokio::time::sleep(Duration::from_millis(50)).await;
ExchangeRates {
base: base.into(),
rates: vec![
("usd".into(), 1.0),
("eur".into(), 0.92),
("jpy".into(), 149.6),
],
}
}
fn build_cache() -> Cache<String, ExchangeRates> {
Cache::builder()
.time_to_live(Duration::from_secs(30)) // 条目级 TTL
.max_capacity(10_000)
.weigher(|_key, value: &ExchangeRates| -> u32 {
value.rates.len() as u32
})
.build()
}
#[tokio::main]
async fn main() {
let cache = build_cache();
// 手动模式:先查后填
if let Some(hit) = cache.get("base-eur").await {
println!("cache hit: {:?} entries", hit.rates.len());
} else {
let fresh = fetch_rates("eur").await;
cache.insert("base-eur".into(), fresh).await;
println!("cache miss: loaded and inserted");
}
// 自动加载模式:未命中时原子执行加载器
let auto = Cache::builder()
.time_to_live(Duration::from_secs(60))
.build();
let rates = auto
.try_get_with(
"base-jpy",
fetch_rates("jpy"),
)
.await
.expect("loader should not fail");
println!("auto-loaded {} rates for {}", rates.rates.len(), rates.base);
println!("entry count: {}", auto.entry_count());
println!("cached weight: {}", auto.weighted_size());
}