.NET 中的缓存
介绍两个主要的缓存, 其参考链接为:
原则上, 缓存是优化技术, 为了让响应更快.
重要:
.NET 中有两个
MemoryCache
类, 一个在Microsoft.Extensions.Caching.Memory
中, 另一个在System.Runtime.Caching
. 本文重点在前一个命名空间中.
Microsoft.Extensions.*
中的类都具有依赖注入特性. 其中 IMemoryCache
, IDistributedCache
可用作服务.
内存中缓存
内存中的缓存, 即 Microsoft.Extensions.Caching.Memory
包.
IMemoryCache
是ConcurrentDictionary<TKey, TValue>
的包装器, 其提供了丰富的 API.其中项为
ICacheEntity
类型, 存储任意的object
类型的数据.
内存中的缓存用于单服务应用. 多服务应用建议使用分布式缓存.
缓存中的 API
缓存的使用可以看成一个 静态字典. 而过期时间可以分为:
- 绝对过期时间
- 滑动过期时间
在超出过期时间的时候缓存的数据被清除掉.
要设置超期, 创建 MemoryCacheEntryOptions
类型的实例, 然后设置相关属性. 超期时间是一个配置.
其文档参考
常用属性与方法为
属性或方法名 | 描述 |
---|---|
AbsoluteExpiration | 设置, 或读取缓存实体的绝对过期时间. |
AbsoluteExpirationRelativeToNow | 设置, 或读取, 据当前的绝对过期时间. |
ExpirationTokens | 获取 IChangeToken 实例, 该实例会影响过期时间. |
RegisterPostEvictionCallBack() | 设置 evict 回调, 在数据超时, 从缓存中移除时触发. |
MemoryCahceEntryOptions opts = new() {
AbsoluteExpirationRelativeToNow = TimeSpan.FromSecond(5)
};
文档中介绍了一些类型与扩展供参考, 细节可以直接查看文档.
内存中的缓存示例(就是用法)
这里是重点
使用缓存:
- 先注册缓存服务. 调用
AddMemoryCache()
方法. - 访问到
IMemoryCache
示例 (例如: 基于构造函数注入). - 使用
Create
或Set
方法来缓存数据.Set
的时候可以带上MemoryCacheEntryOptions
对象配置过期时间. - 可以在
MemoryCacheEntryOptions
中注册事件RegisterPostEvictionCallBack()
, 在数据超期时触发该事件处理函数. - 获取缓存中的数据使用
TryGetValue()
.
示例:
文档给出的示例 采用了基于命令行的程序 (这是另一个版本的 Demo):
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
using IHost host = builder.Build();
IMemoryCache cache =
host.Services.GetRequiredService<IMemoryCache>();
完整代码可参考: 文档
辅助角色服务缓存
该文档提供了一个缓存模型的示例, 将缓存独立成一个后台服务, 这个案例可以看成一个单机版本的分模块的应用模型. 细节可以参考文档
待整理, 抽象出模型.
分布式缓存
分布式缓存将数据存储与进程外部, 适合多实例运行的情况.
注意, 建议只运行在测试环境中.
分布式缓存 API
相比较与内存缓存, 分布式缓存只存储字节数组.
注册使用 AddDistributedMemoryCache
方法.
创建值
涉及 API
IDistributedCache.SetAsync
IDistributedCache.Set
与内存缓存类似, 使用
DistributedCacheEntryOptions
来配置过期时间.虽然提供了 同步版本 与 异步版本的 API. 但是不建议使用同步版本的.
DistributedCacheEntryOptions opts = new () {
AbsoluteExpirationRelativeToNow = TimeSpan.FromSecond(5)
};
// ...
await cache.SetAsync(<key>, <bytes>, opts);
写入扩展方法
封装了一层字符串处理, 不用转 字节数组
DistributedCacheExtensions.SetString
DistributedCacheExtensions.SetStringAsync
读取值
读取返回字节数组, 需要自行转换为字符串.
IDistributedCache.Get
IDistributedCache.GetAsync
读取扩展方法
在字符串上封装了一层
DistributedCacheExtensions.GetString
DistributedCacheExtensions.GetStringAsync
更新值
无法更新值, 只有将其删除, 再重新创建. 但可以刷新其过期时间.
DistributedCache.Refresh
DistributedCache.RefreshAsync
删除值
IDistributedCache.Remove
IDistributedCache.RemoveAsync