分布式缓存
分布式缓存(Distributed Cache) 是一种跨多个节点存储和访问数据的缓存机制。Spire 提供了统一的缓存接口,支持多种过期策略、自动清理机制以及灵活的配置选项。未来,我们将通过集成更成熟的缓存产品来实现该接口,以满足用户多样化的使用场景需求。目前,Spire 内置了一套轻量级的内存缓存实现方案。
快速启动
只需 4 步即可使用分布式缓存:
cangjie
import spire_extensions_caching.*
import spire_extensions_options.*
// 1. 创建默认的内存缓存实例
let cache = MemoryDistributedCache(Options.create(DistributedCacheOptions()))
// 2. 设置缓存项
cache.setString("access_token", "fkgyzj&2j&62jlg==")
// 3. 获取缓存项
let access_token = cache.getString("access_token")
// 4. 删除缓存项
cache.remove("access_token")
过期策略配置
三种不同过期策略选项
策略类型 | 说明 | 典型场景 |
---|---|---|
slidingExpiration 滑动过期 | 每次访问重置计时器 | 会话、Token |
absoluteExpiration 绝对过期 | 固定时间点过期 | 临时验证码 |
absoluteExpirationRelativeToNow 相对过期 | 从设置时刻起计时过期 | 缓存数据 |
绝对过期与相对过期策略
适用于验证码派发场景
cangjie
import std.time.*
import spire_extensions_options.Options
main() {
let cache = MemoryDistributedCache(Options.create(DistributedCacheOptions()))
//多个过期策略,满足其中一个过期策略,就会被标记为垃圾对象
cache.setString("access_token", "fkgyzj&2j&62jlg==", DistributedCacheEntryOptions(
absoluteExpiration: DateTime.now() + Duration.second * 10, // 绝对过期时间点:10秒后
absoluteExpirationRelativeToNow: Duration.second * 5 // 相对过期:5秒后
))
println("初次写入:access_token -> fkgyzj&2j&62jlg==")
sleep(Duration.second * 3)
println("3秒后访问:access_token = " + (cache.getString("access_token") ?? "None"))
sleep(Duration.second * 3)
println("再等3秒访问:access_token = " + (cache.getString("access_token") ?? "None"))
}
bash
初次写入:access_token -> fkgyzj&2j&62jlg==
3秒后访问:access_token = fkgyzj&2j&62jlg==
再等3秒访问:access_token = None
组合策略
使用混合策略时,满足其中一个过期策略即为过期
滑动过期策略
适用于会话场景
cangjie
import std.time.*
import spire_extensions_options.Options
main() {
let cache = MemoryDistributedCache(Options.create(DistributedCacheOptions()))
// 设置滑动过期时长为5s
cache.setString("access_token", "fkgyzj&2j&62jlg==", DistributedCacheEntryOptions(slidingExpiration: Duration.second * 5))
// 有效期限内访问会重置滑动过期计时器
println("初次写入:access_token -> fkgyzj&2j&62jlg==")
sleep(Duration.second * 3)
println("3秒后访问:access_token = " + (cache.getString("access_token") ?? "None"))
// 此时由于滑动计时器被重置为5s,因此任可访问
sleep(Duration.second * 4)
println("再等4秒访问:access_token = " + (cache.getString("access_token") ?? "None"))
//由于距离上次访问时间超过5s,被标记为垃圾对象
sleep(Duration.second * 6)
println("再等6秒访问:access_token = " + (cache.getString("access_token") ?? "None"))
}
bash
初次写入:access_token -> fkgyzj&2j&62jlg==
3秒后访问:access_token = fkgyzj&2j&62jlg==
再等4秒访问:access_token = fkgyzj&2j&62jlg==
再等6秒访问:access_token = None
滑动过期策略内部使用 refresh 刷新缓存项:
垃圾回收
- 访问指定 key 的缓存项时,如果检查为失效会被立即销毁
- 对于没有被访问到的缓存项,如果过期会被标记为垃圾对象,等待内置的销毁线程去回收
- 内置的销毁线程启动策略是任意 key 被访问,并且距离上次扫描时间间隔大于配置的时间间隔(默认为 1 小时)
- 可以通过
DistributedCacheOptions
来配置扫描的时间间隔
性能提示
垃圾回收线程是触发式的,这样做可以达到性能最佳,避免机械式的扫描
容器集成
通过与容器集成可以更方便的通过依赖注入的方式来使用
cangjie
// 原代码
let services = ServiceCollection()
// 注册分布式缓存服务
services.addDistributedMemoryCache{ configureOptions =>
configueOptions.expirationScanFrequency = Duration.second * 180
}
let provider = services.build()
//手动解析服务,或者使用构造注入
let cache = provider.getOrThrow<IDistributedCache>()
说明
后续我们会提供 Redis、MemoryCache 之类的分布式缓存,通过类似 addDistributedRedisCache()
即可替换,无需修改业务代码
最佳实践
- 合理设置过期时间:会话用滑动过期,验证码用相对过期
- 监控内存使用:定期检查缓存项数量,防止溢出
- 依赖注入集成:推荐通过 DI 管理缓存实例
- 错误处理:注意
get
/getString
可能返回None
- 清理频率调整:根据业务需求调整
expirationScanFrequency
注意事项
- 线程安全:所有操作均为线程安全
- 内存限制:大数据量场景需关注内存占用
- 过期策略组合:可同时设置多种过期条件,最先满足者生效
- refresh 仅重置滑动过期,不影响绝对过期
- 删除/过期项自动清理,无需手动干预