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}")键规则提醒
若配置了 cachePrefix,RedisCacheStore 会在收到的键前再拼前缀,手动失效走 cacheStoreInvalidate(同一入口)即可保持一致,不要绕过它直接 DEL。