pub struct Cache { /* private fields */ }Expand description
Cache facade(对齐 PHP think\facade\Cache)
通过全局单例 + 委托 CacheManager 提供 PHP facade 风格 API。
§使用方式
§1. 全局使用(对齐 PHP Cache::set(...) 静态调用)
use sz_rust_cache_facade::{init_default_cache, default_cache, MemoryCacheDriver};
init_default_cache(MemoryCacheDriver::new());
default_cache().set("key", "value", None).unwrap();§2. 独立实例(用于测试隔离)
use sz_rust_cache_facade::{Cache, MemoryCacheDriver};
let cache = Cache::new();
cache.register_default(MemoryCacheDriver::new());
cache.set("key", "value", None).unwrap();Implementations§
Source§impl Cache
impl Cache
Sourcepub fn register_default(&self, driver: MemoryCacheDriver)
pub fn register_default(&self, driver: MemoryCacheDriver)
注册默认驱动
等价于 PHP think\App::get('cache') + 注册默认 store。
Sourcepub fn register_store(
&self,
name: impl Into<String>,
driver: Box<dyn CacheDriver>,
)
pub fn register_store( &self, name: impl Into<String>, driver: Box<dyn CacheDriver>, )
注册命名驱动
Sourcepub fn set_default_store(
&self,
name: impl Into<String>,
) -> Result<(), CacheError>
pub fn set_default_store( &self, name: impl Into<String>, ) -> Result<(), CacheError>
设置默认驱动名
Sourcepub fn set<T: Serialize>(
&self,
key: &str,
value: T,
ttl: Option<Duration>,
) -> Result<(), CacheError>
pub fn set<T: Serialize>( &self, key: &str, value: T, ttl: Option<Duration>, ) -> Result<(), CacheError>
写入缓存(对齐 PHP Cache::set($name, $value, $ttl = null))
PHP Driver::set($name, $value, $ttl = null) 第 110 行:
public function set($name, $value, $expire = null): bool
{
$this->writeTimes++;
if (is_null($expire)) {
$expire = $this->options['expire'];
}
$data = $this->serialize($value);
// ... 写入底层存储
}§参数
key:缓存键value:缓存值(实现Serialize)ttl:过期时间(None永不过期,对齐 PHP$expire = null)
Sourcepub fn get<T: DeserializeOwned>(
&self,
key: &str,
) -> Result<Option<T>, CacheError>
pub fn get<T: DeserializeOwned>( &self, key: &str, ) -> Result<Option<T>, CacheError>
读取缓存(对齐 PHP Cache::get($name, $default = null))
PHP Driver::get($name, $default = null) 第 90 行:
public function get($name, $default = null)
{
$this->readTimes++;
$value = $this->read($name); // 读取原始字节
if (is_null($value)) {
return $default;
}
return $this->unserialize($value); // ⚠️ numeric 返回 string
}§泛型
T = String:对齐 PHPunserialize对 numeric 返回 string 的行为T = Other:通过serde_json::from_str还原
§PHP bug 复刻
PHP unserialize 对 is_numeric 的值返回 string,而非 int。
调用方若想获取 i64,需自行 .parse::<i64>(),对齐 PHP 业务代码
(int) Cache::get('count') 的强转模式。
§参数
key:缓存键
§返回
Ok(Some(value)):缓存命中Ok(None):缓存未命中或已过期
Sourcepub fn get_or<T: DeserializeOwned>(
&self,
key: &str,
default: T,
) -> Result<T, CacheError>
pub fn get_or<T: DeserializeOwned>( &self, key: &str, default: T, ) -> Result<T, CacheError>
读取缓存,未命中时返回默认值(对齐 PHP Cache::get($name, $default))
Sourcepub fn has(&self, key: &str) -> Result<bool, CacheError>
pub fn has(&self, key: &str) -> Result<bool, CacheError>
判断键是否存在(对齐 PHP Cache::has($name))
PHP Driver::has($name) 第 222 行:
public function has($name): bool
{
return $this->read($name) !== null;
}§注意
PHP has 通过 read 检查是否为 null,会同时检查 TTL 过期。
Sourcepub fn inc(&self, key: &str, step: i64) -> Result<i64, CacheError>
pub fn inc(&self, key: &str, step: i64) -> Result<i64, CacheError>
自增(对齐 PHP Cache::inc($name, $step = 1))
PHP Redis 驱动直接 INCRBY;File 驱动读取 → 加减 → 写回。
本驱动默认实现采用 File 驱动行为。
§行为
- 键不存在:初始化为
step - 键存在:解析为 i64 → 加
step→ 写回
Sourcepub fn dec(&self, key: &str, step: i64) -> Result<i64, CacheError>
pub fn dec(&self, key: &str, step: i64) -> Result<i64, CacheError>
自减(对齐 PHP Cache::dec($name, $step = 1))
Sourcepub fn increment(&self, key: &str) -> Result<i64, CacheError>
pub fn increment(&self, key: &str) -> Result<i64, CacheError>
自增 1(便捷方法,对齐 PHP Cache::inc($name) 默认参数)
Sourcepub fn decrement(&self, key: &str) -> Result<i64, CacheError>
pub fn decrement(&self, key: &str) -> Result<i64, CacheError>
自减 1(便捷方法,对齐 PHP Cache::dec($name) 默认参数)
Sourcepub fn pull<T: DeserializeOwned>(
&self,
key: &str,
) -> Result<Option<T>, CacheError>
pub fn pull<T: DeserializeOwned>( &self, key: &str, ) -> Result<Option<T>, CacheError>
读取并删除(对齐 PHP Cache::pull($name, $default = null))
PHP Driver::pull($name, $default = null) 第 332 行:
public function pull(string $name, $default = null)
{
$result = $this->get($name, $default);
$this->delete($name);
return $result;
}Sourcepub fn push<T: Serialize + DeserializeOwned + PartialEq + Clone>(
&self,
key: &str,
value: T,
ttl: Option<Duration>,
) -> Result<(), CacheError>
pub fn push<T: Serialize + DeserializeOwned + PartialEq + Clone>( &self, key: &str, value: T, ttl: Option<Duration>, ) -> Result<(), CacheError>
追加到数组缓存(对齐 PHP Cache::push($name, $value, $expire = null))
PHP Driver::push($name, $value, $expire = null) 第 339-358 行:
public function push(string $name, $value, $expire = null)
{
$data = $this->get($name, []);
if (!is_array($data)) {
$data = [];
}
$data[] = $value;
if (count($data) > 1000) {
array_shift($data);
}
$data = array_unique($data);
$this->set($name, $data, $expire);
return $this;
}§行为
- 缓存不存在 → 创建
vec![value] - 缓存非数组 → 创建
vec![value] - 缓存为数组 → 追加
value - 长度 > 1000 → 丢弃最旧(FIFO)
array_unique去重(保留首次出现的元素)
Sourcepub async fn remember<T, F>(
&self,
key: &str,
ttl: Option<Duration>,
callback: F,
) -> Result<T, CacheError>
pub async fn remember<T, F>( &self, key: &str, ttl: Option<Duration>, callback: F, ) -> Result<T, CacheError>
缓存击穿防护读取(对齐 PHP Cache::remember($name, callable, $expire = null))
PHP Driver::remember 第 287-310 行 + PHP bug 复刻:
- 先
get($name),命中直接返回 - 抢锁
set($name . '_lock', 1)(无 TTL,PHP 源码 bug) - 等待锁释放,200ms 轮询,5 秒超时
- 锁释放后
get($name),命中则返回 - 超时仍未释放:直接调用
callback()(防止永久阻塞) - 抢到锁:调用
callback()→set($name, $data, $expire)→ 释放锁
§PHP bug 复刻
- 锁 key 无 TTL:若进程崩溃,锁永久存在 → 死锁
has()+get()双查 TOCTOU:先has后get
§异步安全
本方法为 async fn,等待锁释放时使用 tokio::time::sleep 让出 worker,
不会阻塞 tokio 运行时。
§与 remember_async 的差异
| 维度 | remember | remember_async |
|---|---|---|
| callback 类型 | FnOnce() -> T(同步) | async fn -> T(异步) |
| 适用场景 | 纯计算 / 已缓存值构造 | IO 密集型回源(DB / HTTP) |
§参数
key:缓存键ttl:缓存过期时间callback:未命中时的回调函数
§异步安全
本方法为 async fn,等待锁释放时使用 tokio::time::sleep 让出 worker,
不会阻塞 tokio 运行时。对齐 Cache::remember_async 的非阻塞行为。
Sourcepub async fn remember_async<T, F, Fut>(
&self,
key: &str,
ttl: Option<Duration>,
callback: F,
) -> Result<T, CacheError>
pub async fn remember_async<T, F, Fut>( &self, key: &str, ttl: Option<Duration>, callback: F, ) -> Result<T, CacheError>
缓存击穿防护读取(异步 callback 版本)
与 Cache::remember 行为一致(同样使用 tokio::time::sleep 让出 worker),
区别在于支持异步 callback,避免在 callback 中执行阻塞 IO 时阻塞 worker。
§参数差异
| 维度 | remember | remember_async |
|---|---|---|
| callback 类型 | FnOnce() -> T(同步) | async fn -> T(异步) |
| 适用场景 | 纯计算 / 已缓存值构造 | IO 密集型回源(DB / HTTP) |
§参数
key:缓存键ttl:缓存过期时间callback:未命中时的异步回调函数
§用法
let user: User = cache.remember_async("user_1", Some(Duration::from_secs(60)), || async {
// 异步回源逻辑(如 DB 查询)
User::find_async(1).await
}).await?;Sourcepub fn clear(&self) -> Result<(), CacheError>
pub fn clear(&self) -> Result<(), CacheError>
清空所有缓存(对齐 PHP Cache::clear())
Sourcepub fn delete_many(&self, keys: &[&str]) -> Result<(), CacheError>
pub fn delete_many(&self, keys: &[&str]) -> Result<(), CacheError>
批量删除多个缓存 key(对齐 PHP Driver::deleteMultiple($keys): bool)
PHP think\cache\Driver::deleteMultiple 第 342-351 行:
public function deleteMultiple($keys): bool
{
foreach ($keys as $key) {
$result = $this->delete($key);
if (false === $result) {
return false;
}
}
return true;
}§PHP 行为对齐
- 逐个调用
delete(key),任一失败立即返回Err - 对齐 PHP
if (false === $result) return false - 注意:PHP
File::delete文件不存在也返回false,导致deleteMultiple在文件不存在时也返回false(PHP bug)。Rust 端delete对不存在的 key 返回Ok(()),因此delete_many对不存在的 key 不会失败(修正 PHP bug)。
§业务场景对齐
对齐业务场景 4(一次写操作失效多类缓存):
// addons/sdp/model/Category.php
Cache::delete('sdp_category_tree');
Cache::delete('sdp_category_select');
Cache::delete('sdp_category_child');
Cache::delete('sdp_category_nav');
Cache::delete('sdp_category_info:'.$data['cat_id']);Rust 端用 delete_many 一次调用:
cache.delete_many(&["sdp_category_tree", "sdp_category_select", "sdp_category_child"])?;Sourcepub fn invalidate_after_write(&self, keys: &[&str]) -> Result<(), CacheError>
pub fn invalidate_after_write(&self, keys: &[&str]) -> Result<(), CacheError>
写操作后失效缓存(对齐 PHP 业务场景 1:事务内写后失效)
PHP 业务代码典型模式(app/food/model/cashier/Clerk.php):
public function add($data): bool
{
$this->startTrans();
try {
if($this->save($data)){
Cache::delete('foodCashierClerkAll_' . $data['cashier_id']);
$this->commit();
}
} catch (\Exception $e) {
$this->rollback();
}
}§设计决策
- 严禁直接更新缓存:写操作后应
delete(让下次get时回源), 而非set更新缓存值(cache-aside 模式) - 返回
Result<(), CacheError>:调用方可选择忽略错误(对齐 PHP fire and forget) - 与
delete_many的区别:invalidate_after_write语义明确(写后失效), 便于代码审查和日志追踪
§用法
// 写操作后失效相关缓存
cache.invalidate_after_write(&["foodCashierClerkAll_1", "foodCashierClerkList_1"])?;
// 或 fire and forget(对齐 PHP 业务代码不检查返回值)
let _ = cache.invalidate_after_write(&["foodCashierClerkAll_1"]);Sourcepub fn refresh<T, F>(
&self,
key: &str,
ttl: Option<Duration>,
fetcher: F,
) -> Result<T, CacheError>
pub fn refresh<T, F>( &self, key: &str, ttl: Option<Duration>, fetcher: F, ) -> Result<T, CacheError>
先删后读强制刷新(对齐 PHP 业务场景 2:delete → get → 回源 set)
PHP 业务代码典型模式(app/common/model/store/Store.php):
public static function info($store_id){
$cacheKey = 'wmall_store_info_'.$store_id;
Cache::delete($cacheKey); // 先删
$info = Cache::get($cacheKey); // 再读(必为空,触发回源)
if(!$info){
$info = $model->with(['supplier','nav'])->find();
if($info){
Cache::set($cacheKey, $info, 86400);
}
}
return $info;
}§设计决策
- 优化 PHP 模式:
delete → fetcher() → set,避免一次无意义的get(PHP 模式中delete后get必为空,直接调用fetcher更高效) - 严禁直接更新缓存:通过
delete+fetcher+set实现“强制刷新“, 而非直接set覆盖(确保 fetcher 是唯一数据源) - 返回
Result<T, CacheError>:fetcher 失败时传播错误,不写入缓存
§用法
let store_info: StoreInfo = cache.refresh("wmall_store_info_1", Some(Duration::from_secs(86400)), || {
// 回源逻辑
Ok(StoreInfo::find(1))
})?;Sourcepub fn fetch_singleflight<T, F>(
&self,
key: &str,
ttl: Option<Duration>,
fetcher: F,
) -> Result<T, CacheError>
pub fn fetch_singleflight<T, F>( &self, key: &str, ttl: Option<Duration>, fetcher: F, ) -> Result<T, CacheError>
singleflight 模式回源(Rust 特有扩展,防止缓存击穿)
同一 key 并发请求时,只允许一个线程回源,其他线程等待锁释放后 通过 double-check 从缓存读取结果。
§与 PHP remember 的差异
| 维度 | PHP remember | Rust fetch_singleflight |
|---|---|---|
| 加锁方式 | $this->set($name.'_lock', true) 非原子 | parking_lot::Mutex::lock() 原子互斥 |
| 锁 TTL | 无(进程崩溃永久锁死) | 无需 TTL(Mutex guard 释放即解锁,panic 自动释放) |
| 等待方式 | while + usleep(200ms) 轮询 5s 超时 | Mutex::lock() 阻塞等待(无超时,但 panic 自动释放) |
| double-check | 无 | 有(获取锁后再次检查缓存) |
§用法
let value: String = cache.fetch_singleflight("hot_key", Some(Duration::from_secs(60)), || {
// 回源逻辑(数据库查询等)
Ok("expensive_value".to_string())
})?;Sourcepub fn set_with_jitter<T>(
&self,
key: &str,
value: &T,
ttl: Option<Duration>,
jitter: Duration,
) -> Result<(), CacheError>
pub fn set_with_jitter<T>( &self, key: &str, value: &T, ttl: Option<Duration>, jitter: Duration, ) -> Result<(), CacheError>
设置带随机抖动的 TTL(Rust 特有扩展,防止缓存雪崩)
在 TTL 上加 [0, jitter] 范围的随机抖动,避免大量 key 同时过期触发雪崩。
§设计决策
- PHP 无随机过期时间机制(
getExpireTime不做 TTL 抖动) - Rust 特有扩展:用
randcrate 生成随机抖动 - 实际 TTL 在
[ttl, ttl + jitter]范围内 jitter为 0 时等价于set(无抖动)ttl为None时等价于永久缓存(无抖动)
§用法
// 基础 TTL 60s + 随机抖动 0-10s(实际 TTL 60-70s)
cache.set_with_jitter("key", "value", Some(Duration::from_secs(60)), Duration::from_secs(10))?;Sourcepub fn fetch_with_protection<T, F>(
&self,
key: &str,
ttl: Option<Duration>,
jitter: Duration,
fetcher: F,
) -> Result<T, CacheError>
pub fn fetch_with_protection<T, F>( &self, key: &str, ttl: Option<Duration>, jitter: Duration, fetcher: F, ) -> Result<T, CacheError>
Sourcepub fn with_store<R, F>(&self, name: &str, f: F) -> Result<R, CacheError>
pub fn with_store<R, F>(&self, name: &str, f: F) -> Result<R, CacheError>
Sourcepub fn tag(&self, name: &str) -> TagSet<'_>
pub fn tag(&self, name: &str) -> TagSet<'_>
缓存标签(对齐 PHP Driver::tag($name))
PHP Driver::tag($name) 第 196-206 行:
public function tag($name): TagSet
{
$name = (array) $name;
$key = implode('-', $name);
if (!isset($this->tag[$key])) {
$this->tag[$key] = new TagSet($name, $this);
}
return $this->tag[$key];
}§PHP 单例 vs Rust 实现
PHP 使用 $this->tag[$key] 单例缓存 TagSet 对象,避免重复创建。
Rust 端不实现单例(TagSet 是无状态结构体,每次创建行为一致),
功能上完全等价。
§示例
cache.tag("user").set("user:1", &data, None)?;
cache.tag("user").clear(); // 清除所有 user 标签下的缓存Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Cache
impl !RefUnwindSafe for Cache
impl !UnwindSafe for Cache
impl Send for Cache
impl Sync for Cache
impl Unpin for Cache
impl UnsafeUnpin for Cache
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more