Skip to content

Redis 分布式缓存

@Cacheable 宏有两种形态:

形态后端适用
@Cacheable[ttlMs]进程内 TtlCache(每方法私有)单实例、任意返回类型
@Cacheable[ttlMs, "redis"]命名 CacheStore 后端多实例共享、返回类型限 String

用法

cangjie
import ace_framework.*
import ace_framework_macros.*
import ace_redis.*   // RedisComponent 注册名为 "redis" 的 CacheStore

@Service
public class PriceService {
    // 首次执行原方法并 SET(PX 5000),5 秒内所有实例命中走 GET
    @Cacheable[5000, "redis"]
    public func priceOf(sku: String): String {
        // 昂贵计算/远程调用……结果须为 String(结构化数据先 JSON 化)
        "{\"sku\":\"${jsonEscape(sku)}\",\"price\":99}"
    }
}

约束与规则

  • 返回类型必须是 String——仓颉零反射,框架不做任意类型的通用序列化;结构化结果先手动 JSON 化。违反时编译期报错

    @Cacheable[ttlMs, "store"] 命名后端仅支持返回类型 String(收到: 'Int64')
  • 缓存键 = ace:cache:<方法名>|<参数 toString 用 "|" 拼接>,再叠加 [redis].cachePrefix 全局前缀。方法名入键避免不同方法共享 Redis 键空间时互相污染;多应用共享同一 Redis 实例时用 cachePrefix 隔离。

  • TTL:首参毫秒数,写入即 SET ... PX ttl,由 Redis 负责过期。

  • 单参形态行为与既有版本完全一致,升级不影响存量代码。

CacheStore 命名后端

双参形态经 ace_framework_runtime 的命名后端注册表解析,任何组件都可注册自己的实现:

cangjie
public interface CacheStore {
    func get(key: String): ?String
    func put(key: String, v: String, ttlMs: Int64): Unit   // ttlMs <= 0 视为永久
    func invalidate(key: String): Unit
}

registerCacheStore("redis", myStore)                  // RedisComponent 已自动注册 "redis"
cacheStoreGet("redis", key)                           // 手动读
cacheStorePut("redis", key, v, ttlMs)                 // 手动写
cacheStoreInvalidate("redis", key)                    // 手动失效

回退语义:按名解析不到注册后端时,自动回退到按名惰性创建的进程内 TtlCache<String>——没引入 ace-redis(或 Redis 组件被禁用)的应用,@Cacheable[ttl, "redis"] 依然能跑,只是退化为本地缓存。

故障语义RedisCacheStore):Redis 异常时 get 返回 None(视为未命中,回源执行原方法)、put/invalidate 吞异常打日志——缓存故障不放大为业务故障。

手动失效

@Cacheable 不自动失效(写路径更新数据后旧缓存在 TTL 内仍可见)。需要即时失效时按键规则手动调用:

cangjie
// 与宏生成的键规则一致:ace:cache:<方法名>|<参数>
cacheStoreInvalidate("redis", "ace:cache:priceOf|${sku}")

键规则提醒

若配置了 cachePrefixRedisCacheStore 会在收到的键前再拼前缀,手动失效走 cacheStoreInvalidate(同一入口)即可保持一致,不要绕过它直接 DEL

基于 Apache-2.0 许可证发布