| Crates.io | secra-cache |
| lib.rs | secra-cache |
| version | 0.1.2 |
| created_at | 2025-12-30 08:39:42.388438+00 |
| updated_at | 2025-12-30 08:56:59.987849+00 |
| description | 一个基于 Redis 的统一缓存管理库,专为插件化架构设计,提供强隔离、生命周期管理和极简 API |
| homepage | |
| repository | |
| max_upload_size | |
| id | 2012297 |
| size | 113,836 |
一个基于 Redis 的统一缓存管理库,专为插件化架构设计,提供强隔离、生命周期管理和极简 API。
CacheManager,插件不能直接访问 Redis在 Cargo.toml 中添加依赖:
[dependencies]
secra-cache = "0.1.2"
项目提供了一个完整的示例,展示所有主要功能:
# 确保 Redis 服务正在运行(默认 localhost:6379)
# 或设置环境变量指定 Redis 地址
export REDIS_URL="redis://localhost:6379"
# 运行示例
cargo run --example basic_demo
示例演示了以下功能:
use secra_cache::CacheManager;
// 1. 创建 CacheManager
let cache_manager = CacheManager::new_with_defaults(
"redis://localhost:6379",
).await?;
// 2. 为插件创建 Cache 实例
let plugin_cache = cache_manager.create_plugin_cache("user_plugin".to_string());
use secra_cache::Cache;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct User {
id: u64,
name: String,
}
// 1. 设置缓存
let user = User {
id: 123,
name: "Alice".to_string(),
};
plugin_cache.set("user:123", &user, None).await?;
// 2. 获取缓存
let user: Option<User> = plugin_cache.get("user:123").await?;
// 3. 删除缓存
plugin_cache.delete("user:123").await?;
// 4. 检查缓存是否存在
let exists = plugin_cache.exists("user:123").await?;
// 5. 清理所有缓存
plugin_cache.clear().await?;
// 6. 按模块清理缓存
plugin_cache.clear_module("user").await?; // 清理 user 模块的所有缓存
基座系统负责创建和管理 CacheManager 实例,并为每个插件创建独立的缓存实例。
use secra_cache::{CacheManager, CacheConfig};
// 方式 1: 使用默认配置
let cache_manager = CacheManager::new_with_defaults(
"redis://localhost:6379"
).await?;
// 方式 2: 使用自定义配置
let config = CacheConfig {
redis_url: "redis://localhost:6379".to_string(),
key_prefix: "secra".to_string(),
default_ttl: Some(3600), // 默认 TTL 1 小时
// ... 其他配置
};
let cache_manager = CacheManager::new(config).await?;
// 为插件创建缓存实例
let plugin_cache = cache_manager.create_plugin_cache("user_plugin".to_string());
// 插件卸载时,清理该插件的所有缓存
cache_manager.remove_plugin_cache("user_plugin").await?;
插件通过 Cache trait 提供的 API 进行缓存操作,无需关心 Redis 细节。
use secra_cache::Cache;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
struct Product {
id: u64,
name: String,
price: f64,
}
// 设置缓存(无 TTL)
let product = Product {
id: 1,
name: "iPhone".to_string(),
price: 9999.0,
};
cache.set("product:1", &product, None).await?;
// 设置缓存(带 TTL,单位:秒)
cache.set("product:1", &product, Some(3600)).await?;
// 获取缓存
match cache.get::<Product>("product:1").await? {
Some(product) => println!("找到产品: {:?}", product),
None => println!("缓存未命中"),
}
// 删除缓存
cache.delete("product:1").await?;
// 检查缓存是否存在
if cache.exists("product:1").await? {
println!("缓存存在");
}
// 批量设置
let mut items = Vec::new();
items.push(("key1", "value1"));
items.push(("key2", "value2"));
cache.set_many(&items, Some(3600)).await?;
// 批量获取
let keys = vec!["key1", "key2"];
let values: Vec<Option<String>> = cache.get_many(&keys).await?;
// 批量删除
cache.delete_many(&keys).await?;
// 按模块设置缓存(推荐)
cache.set("user:123", &user, None).await?; // user 模块
cache.set("order:456", &order, None).await?; // order 模块
// 清理特定模块的所有缓存
cache.clear_module("user").await?; // 只清理 user 模块的缓存
// 清理所有缓存
cache.clear().await?; // 清理该插件的所有缓存
// 设置 TTL
cache.set_ttl("key", 3600).await?;
// 获取剩余 TTL(秒)
let ttl = cache.get_ttl("key").await?;
match ttl {
Some(seconds) => println!("剩余 TTL: {} 秒", seconds),
None => println!("Key 不存在或没有设置 TTL"),
}
// 移除 TTL(使 Key 永久有效)
cache.remove_ttl("key").await?;
所有 Redis Key 必须遵循以下格式:
{system}:{plugin_id}:{biz}:{key}
{system}: 系统前缀,默认 secra,可通过配置修改{plugin_id}: 插件 ID,由基座系统传入{biz}: 业务模块标识,建议使用业务领域名称(如 user、order){key}: 具体的缓存键secra:user_plugin:user:123
secra:order_service:order:2024:001
secra:payment_plugin:payment:txn:abc123
biz):使用小写字母和下划线,如 user_profile、order_listkey):使用有意义的标识符,如用户 ID、订单号等biz 前缀use secra_cache::CacheConfig;
let config = CacheConfig {
redis_url: "redis://localhost:6379".to_string(),
key_prefix: "secra".to_string(), // Key 前缀
default_ttl: Some(3600), // 默认 TTL(秒),None 表示永久
connection_timeout: Some(5000), // 连接超时(毫秒)
// ... 其他配置项
};
可以通过环境变量配置 Redis 连接:
export REDIS_URL="redis://localhost:6379"
export REDIS_KEY_PREFIX="secra"
export REDIS_DEFAULT_TTL="3600"
Cache trait 定义了插件可用的所有缓存操作:
#[async_trait]
pub trait Cache: Send + Sync {
/// 设置缓存
async fn set<T: Serialize + Send + Sync>(
&self,
key: &str,
value: &T,
ttl: Option<u64>,
) -> Result<(), CacheError>;
/// 获取缓存
async fn get<T: DeserializeOwned + Send + Sync>(
&self,
key: &str,
) -> Result<Option<T>, CacheError>;
/// 删除缓存
async fn delete(&self, key: &str) -> Result<(), CacheError>;
/// 检查缓存是否存在
async fn exists(&self, key: &str) -> Result<bool, CacheError>;
/// 设置 TTL
async fn set_ttl(&self, key: &str, ttl: u64) -> Result<(), CacheError>;
/// 获取剩余 TTL
async fn get_ttl(&self, key: &str) -> Result<Option<u64>, CacheError>;
/// 移除 TTL
async fn remove_ttl(&self, key: &str) -> Result<(), CacheError>;
/// 批量设置
async fn set_many<T: Serialize + Send + Sync>(
&self,
items: &[(&str, &T)],
ttl: Option<u64>,
) -> Result<(), CacheError>;
/// 批量获取
async fn get_many<T: DeserializeOwned + Send + Sync>(
&self,
keys: &[&str],
) -> Result<Vec<Option<T>>, CacheError>;
/// 批量删除
async fn delete_many(&self, keys: &[&str]) -> Result<(), CacheError>;
/// 清理所有缓存
async fn clear(&self) -> Result<(), CacheError>;
/// 清理指定模块的缓存
async fn clear_module(&self, module: &str) -> Result<(), CacheError>;
}
impl CacheManager {
/// 创建 CacheManager(使用默认配置)
pub async fn new_with_defaults(redis_url: &str) -> Result<Self, CacheError>;
/// 创建 CacheManager(使用自定义配置)
pub fn new(config: CacheConfig) -> Result<Self, CacheError>;
/// 为插件创建缓存实例
pub fn create_plugin_cache(&self, plugin_id: String) -> PluginCache;
/// 移除插件缓存(清理该插件的所有缓存)
pub async fn remove_plugin_cache(&self, plugin_id: &str) -> Result<(), CacheError>;
}
所有操作可能返回 CacheError:
use secra_cache::CacheError;
match cache.get::<User>("user:123").await {
Ok(Some(user)) => println!("找到用户: {:?}", user),
Ok(None) => println!("缓存未命中"),
Err(CacheError::RedisError(e)) => eprintln!("Redis 错误: {}", e),
Err(CacheError::SerializationError(e)) => eprintln!("序列化错误: {}", e),
Err(e) => eprintln!("其他错误: {}", e),
}
RedisError: Redis 连接或操作错误SerializationError: 序列化/反序列化错误InvalidKeyError: Key 格式错误TimeoutError: 操作超时// ✅ 推荐:使用模块前缀
cache.set("user:123", &user, None).await?;
cache.set("user:profile:123", &profile, None).await?;
cache.set("order:456", &order, None).await?;
// ❌ 不推荐:扁平化 Key
cache.set("123", &user, None).await?;
cache.set("456", &order, None).await?;
// 根据数据特性设置 TTL
cache.set("user:123", &user, Some(3600)).await?; // 用户数据:1 小时
cache.set("hot_news:1", &news, Some(300)).await?; // 热点新闻:5 分钟
cache.set("config:app", &config, None).await?; // 配置数据:永久
// ✅ 推荐:批量操作
let keys = vec!["user:1", "user:2", "user:3"];
let users: Vec<Option<User>> = cache.get_many(&keys).await?;
// ❌ 不推荐:循环单个操作
for key in keys {
let user = cache.get::<User>(key).await?;
// ...
}
// 基座系统:插件卸载时
cache_manager.remove_plugin_cache("user_plugin").await?;
match cache.get::<User>("user:123").await? {
Some(user) => Ok(user),
None => {
// 从数据库加载
let user = load_user_from_db(123).await?;
// 写入缓存
cache.set("user:123", &user, Some(3600)).await?;
Ok(user)
}
}
// ✅ 推荐:使用强类型
#[derive(Serialize, Deserialize)]
struct User { /* ... */ }
cache.set("user:123", &user, None).await?;
let user: Option<User> = cache.get("user:123").await?;
// ❌ 不推荐:使用字符串
cache.set("user:123", &user_json_string, None).await?;
详细的设计文档请参考项目文档目录(如果存在):
01-架构设计.md:整体架构和调用关系02-Key设计规范.md:Key 命名规范和隔离机制03-核心能力设计.md:get/set/del/exists、TTL、序列化04-生命周期管理.md:插件卸载/升级时的缓存清理05-接口设计.md:Cache trait、CacheManager、使用示例06-进阶能力.md:批量操作、分布式锁、防穿透/雪崩07-反模式与约束.md:禁止行为和强制约束08-实现指南.md:实现步骤和最佳实践主要依赖:
redis: Redis 客户端serde / serde_json: 序列化/反序列化tokio: 异步运行时async-trait: 异步 trait 支持dashmap: 并发数据结构tracing: 结构化日志thiserror: 错误处理详细依赖信息请参考 DEPENDENCIES.md。
本项目采用双重许可证:
您可以选择任一许可证使用本项目。
欢迎提交 Issue 和 Pull Request!