secra-cache

Crates.iosecra-cache
lib.rssecra-cache
version0.1.2
created_at2025-12-30 08:39:42.388438+00
updated_at2025-12-30 08:56:59.987849+00
description一个基于 Redis 的统一缓存管理库,专为插件化架构设计,提供强隔离、生命周期管理和极简 API
homepage
repository
max_upload_size
id2012297
size113,836
(BadassFree)

documentation

README

Secra Cache

一个基于 Redis 的统一缓存管理库,专为插件化架构设计,提供强隔离、生命周期管理和极简 API。

📋 目录

✨ 功能特性

  • 统一治理:所有缓存操作必须通过 CacheManager,插件不能直接访问 Redis
  • 强隔离:每个插件拥有独立的命名空间,避免 Key 冲突
  • 生命周期管理:插件卸载/升级时自动清理相关缓存
  • 极简 API:插件侧 API 简单易用,隐藏 Redis 细节
  • 可扩展性:支持未来扩展(配额、监控、限流等)
  • 异步支持:基于 Tokio 异步运行时,高性能
  • 类型安全:利用 Rust 类型系统,编译时保证安全

🚀 快速开始

安装

Cargo.toml 中添加依赖:

[dependencies]
secra-cache = "0.1.2"

运行示例

项目提供了一个完整的示例,展示所有主要功能:

# 确保 Redis 服务正在运行(默认 localhost:6379)
# 或设置环境变量指定 Redis 地址
export REDIS_URL="redis://localhost:6379"

# 运行示例
cargo run --example basic_demo

示例演示了以下功能:

  • 创建 CacheManager 和插件缓存实例
  • 基本缓存操作(set、get、delete、exists)
  • TTL 管理(设置、查询、延长过期时间)
  • 循环操作(模拟批量操作)
  • 模块化缓存管理
  • 插件生命周期管理
  • Redis 连接健康检查
  • 错误处理

基本使用

基座系统

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 实例,并为每个插件创建独立的缓存实例。

创建 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 管理

// 设置 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?;

🔑 Key 格式规范

所有 Redis Key 必须遵循以下格式:

{system}:{plugin_id}:{biz}:{key}

格式说明

  • {system}: 系统前缀,默认 secra,可通过配置修改
  • {plugin_id}: 插件 ID,由基座系统传入
  • {biz}: 业务模块标识,建议使用业务领域名称(如 userorder
  • {key}: 具体的缓存键

示例

secra:user_plugin:user:123
secra:order_service:order:2024:001
secra:payment_plugin:payment:txn:abc123

命名建议

  • 业务模块 (biz):使用小写字母和下划线,如 user_profileorder_list
  • 缓存键 (key):使用有意义的标识符,如用户 ID、订单号等
  • 避免冲突:不同业务模块使用不同的 biz 前缀

⚙️ 配置说明

CacheConfig

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"

🛠️ API 参考

Cache Trait

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>;
}

CacheManager

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

所有操作可能返回 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: 操作超时

💡 最佳实践

1. 使用模块化 Key 设计

// ✅ 推荐:使用模块前缀
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?;

2. 合理设置 TTL

// 根据数据特性设置 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?;          // 配置数据:永久

3. 使用批量操作提高性能

// ✅ 推荐:批量操作
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?;
    // ...
}

4. 插件卸载时清理缓存

// 基座系统:插件卸载时
cache_manager.remove_plugin_cache("user_plugin").await?;

5. 处理缓存未命中

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)
    }
}

6. 类型安全

// ✅ 推荐:使用强类型
#[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!

📞 联系方式

Commit count: 0

cargo fmt