概念
什么是缓存,在我的项目中,为了进步数据的读取速度,咱们会对不常常变更但拜访频繁的数据做缓存解决,咱们罕用的缓存有:
-
本地缓存
- 内存缓存:IMemoryCache
-
分布式缓存
- Redis: StackExchange.Redis
性能
目前,MasaFramework
为咱们提供了以下能力
-
IDistributedCacheClient: 分布式缓存
- Masa.Contrib.Caching.Distributed.StackExchangeRedis: 基于 StackExchange.Redis 实现的分布式缓存
-
IMultilevelCacheClient: 多级缓存
- Masa.Contrib.Caching.MultilevelCache: 基于内存缓存以及分布式缓存实现的多级缓存,反对监控缓存变更,分布式缓存更新后相应的内存缓存也会同步更新,防止命中过期的内存缓存导致获取谬误的数据,同时也尽可能的将多个正本的内存缓存放弃同步
入门
- 前提条件:装置.NET 6.0
分布式缓存
- 新建 ASP.NET Core 空我的项目
Assignment.DistributedCache
,并装置Masa.Contrib.Caching.Distributed.StackExchangeRedis
dotnet new web -o Assignment.DistributedCache
cd Assignment.DistributedCache
dotnet add package Masa.Contrib.Caching.Distributed.StackExchangeRedis --version 0.6.0-rc.5
- 配置
Redis
配置信息
{
"RedisConfig":{
"Servers":[
{
"Host":"localhost",
"Port":6379
}
],
"DefaultDatabase":3,
"ConnectionPoolSize":10
}
}
- 注册分布式缓存,并应用
Redis
缓存,批改Program.cs
var builder = WebApplication.CreateBuilder(args);
// 注册分布式缓存
builder.Services.AddDistributedCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();// 应用分布式 Redis 缓存, 默认应用本地 `RedisConfig` 下的配置
});
应用分布式缓存的数据起源默认为
IOptionsMonitor<RedisConfigurationOptions>
,如果本地未正确在RedisConfig
节点配置缓存信息,且我的项目中也没有通过其它形式配置使其反对选项模式,则默认应用的 Redis 配置为:地址: localhost、端口:6379,明码:空,数据库:db0
- 新建
User
类,用于接管用户信息
public class User
{public string Name { get; set;}
public int Age {get; set;}
}
- 如何应用
IDistributedCacheClient
,批改Program.cs
// 设置缓存
app.MapPost("/set/{id}", async (IDistributedCacheClient distributedCacheClient, [FromRoute] string id, [FromBody] User user) =>
{await distributedCacheClient.SetAsync(id, user);
return Results.Accepted();});
// 获取缓存
app.MapGet("/get/{id}", async (IDistributedCacheClient distributedCacheClient, [FromRoute] string id) =>
{var value = await distributedCacheClient.GetAsync<User>(id);
return Results.Ok(value);
});
多级缓存
- 新建 ASP.NET Core 空我的项目
Assignment.DistributedCache
,并装置Masa.Contrib.Caching.MultilevelCache
、Masa.Contrib.Caching.Distributed.StackExchangeRedis
dotnet new web -o Assignment.MultilevelCache
cd Assignment.MultilevelCache
dotnet add package Masa.Contrib.Caching.MultilevelCache --version 0.6.0-rc.5
dotnet add package Masa.Contrib.Caching.Distributed.StackExchangeRedis --version 0.6.0-rc.5
- 注册多级缓存,并应用分布式
Redis
缓存,批改Program.cs
var builder = WebApplication.CreateBuilder(args);
// 注册多级缓存
builder.Services.AddMultilevelCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();// 应用分布式 Redis 缓存
});
- 新建
User
类,用于接管用户信息
public class User
{public string Name { get; set;}
public int Age {get; set;}
}
- 如何应用
IMultilevelCacheClient
,批改Program.cs
// 设置缓存
app.MapPost("/set/{id}", async (IMultilevelCacheClient multilevelCacheClient, [FromRoute] string id, [FromBody] User user) =>
{await multilevelCacheClient.SetAsync(id, user);
return Results.Accepted();});
// 获取缓存
app.MapGet("/get/{id}", async (IMultilevelCacheClient multilevelCacheClient, [FromRoute] string id) =>
{var value = await multilevelCacheClient.GetAsync<User>(id);
return Results.Ok(value);
});
测试
借助 Postman
或者 Swagger
或者应用其它 API 测试工具,别离测试设置缓存与获取缓存,以验证分布式缓存以及多级缓存是能够失常应用的。
情谊提醒:查看 Redis 缓存,找到刚刚你配置的缓存,确定下它的存储后果是否与你设想的统一!!
规定
通过测试,咱们的分布式缓存与多级缓存是能够失常应用的,但查看 Redis 的存储后果后,发现它们理论的存储与咱们心目中的后果如同是有点出入,它们别离是:
- 缓存 Key 不同 (与咱们设置的 Key 不完全一致)
- 构造不同 (理论存储的为 Hash 类型)
- 内容不同 (内容通过压缩)
缓存 Key 的生成规定
缓存 Key 反对三种规定:
枚举 | 值 | 形容 |
---|---|---|
None | 1 | 不做解决,传入的 Key 即为理论的缓存 Key |
TypeName | 2 | 理论的缓存 Key = $”{GetTypeName(T)}.{传入缓存 Key}” (默认) |
TypeAlias | 3 | 依据 TypeName 失去对应的别名与 Key 的组合,Format: ${TypeAliasName}{:}{key} |
具体规定可查看
存储构造与规定
Masa.Contrib.Caching.Distributed.StackExchangeRedis 应用的是 Hash 存储,通过应用 Hash 存储,反对缓存的相对过期以及绝对过期,其中:
键 | 形容 | 具体 | 非凡 |
---|---|---|---|
absexp | 相对过期工夫的 Ticks | 自公历 0001-01-01 00:00:00:000 到相对过期工夫的计时周期数 (1 周期 = 100ns 即 1/10000 ms) |
-1 为永不过期 |
sldexp | 滑动过期工夫的 Ticks | 自公历 0001-01-01 00:00:00:000 到滑动过期工夫的计时周期数 (1 周期 = 100ns 即 1/10000 ms,每次获取数据时会刷新滑动过期工夫) |
-1 为永不过期 |
data | 数据 | 存储用户设置的缓存数据 |
内容压缩规定
- 当存储值类型为以下类型时,不对数据进行压缩:
- Byte
- SByte
- UInt16
- UInt32
- UInt64
- Int16
- Int32
- Int64
- Double
- Single
- Decimal
- 当存储值类型为字符串时,对数据进行压缩
- 当存储值类型不满足以上条件时,对数据进行序列化并进行压缩
分布式 Redis 缓存示例
分布式缓存注册
计划一. 通过本地配置文件注册
- 批改
appsettings.json
文件
{
"RedisConfig":{
"Servers":[
{
"Host":"localhost",
"Port":6379
}
],
"DefaultDatabase":3,
"ConnectionPoolSize":10
}
}
- 注册分布式 Redis 缓存
builder.Services.AddDistributedCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();
});
计划二. 手动指定 Redis 配置注册
builder.Services.AddDistributedCache(distributedCacheOptions =>
{
distributedCacheOptions.UseStackExchangeRedisCache(options =>
{options.Servers = new List<RedisServerOptions>()
{new("localhost", 6379)
};
options.DefaultDatabase = 3;
options.ConnectionPoolSize = 10;
options.GlobalCacheOptions = new CacheOptions()
{CacheKeyType = CacheKeyType.None // 全局禁用缓存 Key 格式化解决};
});
});
计划三. 通过选项模式注册
- 通过 Configure 办法使其反对选项模式
builder.Services.Configure<RedisConfigurationOptions>(redisConfigurationOptions =>
{redisConfigurationOptions.Servers = new List<RedisServerOptions>()
{new("localhost", 6379)
};
redisConfigurationOptions.DefaultDatabase = 3;
redisConfigurationOptions.ConnectionPoolSize = 10;
redisConfigurationOptions.GlobalCacheOptions = new CacheOptions()
{CacheKeyType = CacheKeyType.None};
});
- 注册分布式 Redis 缓存
builder.Services.AddDistributedCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();
});
计划四. 通过指定 Configuration
注册
- 在 Redis 缓存的配置存储到本地
appsettings.json
文件
{
"RedisConfig":{
"Servers":[
{
"Host": "localhost",
"Port": 6379
}
],
"DefaultDatabase": 3,
"ConnectionPoolSize": 10
}
}
- 指定
Configuration
注册分布式 Redis 缓存
var builder = WebApplication.CreateBuilder(args);
// 注册分布式缓存
builder.Services.AddDistributedCache(distributedCacheOptions =>
{
// 应用存储 Redis 配置的 Configuration
distributedCacheOptions.UseStackExchangeRedisCache(builder.Configuration.GetSection("RedisConfig"));
});
计划五. 将配置存储到 Dcc 上,并通过 Configuration 提供的手动映射性能,实现选项模式
- 应用 Dcc,并手动指定映射
builder.AddMasaConfiguration(configurationBuilder =>
{configurationBuilder.UseDcc();// 应用 Dcc 扩大 Configuration 能力,反对近程配置
configurationBuilder.UseMasaOptions(options =>
{
// 通过手动映射 RedisConfigurationOptions 的配置,实现选项模式
options.MappingConfigurationApi<RedisConfigurationOptions>("{替换为 Dcc 中配置所属的 AppId}", "{替换为 Redis 配置的对象名称}");
});
});
- 注册分布式 Redis 缓存
builder.Services.AddDistributedCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();
});
计划三、四、五的实质都是通过反对选项模式来注册分布式 Redis 缓存
批改缓存 Key 映射规定
批改缓存 Key 映射规定非常简略,咱们在配置时更改 CacheKeyType 为对应的规定即可,但当 CacheKeyType = 3 须要留神,它须要额定提供 类型名与别名的对应关系,残缺例子如下:
- 批改
appsettings.json
, 将 CacheKeyType 的值改为 3
{
"RedisConfig":{
"Servers":[
{
"Host":"localhost",
"Port":6379
}
],
"DefaultDatabase":3,
"ConnectionPoolSize":10,
"GlobalCacheOptions": {"CacheKeyType": 3 //CacheKeyType 为 3 时启用别名格式化缓存 Key,可节俭缓存 Key 的键长度}
}
}
- 注册分布式缓存并配置类型名与别名的对应关系
builder.Services.AddDistributedCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();
}, typeAliasOptions =>
{typeAliasOptions.GetAllTypeAliasFunc = () => new Dictionary<string, string>()
{{ "String", "s"}// 当类型为 String 时,格式化后的 Key 为 s:key
};
});
通过指定类型与别名的对应关系,从而使得最终造成较短的缓存 Key,以达到节俭存储空间的目标,缓存 Key 生成规定可查看
多级缓存示例
多级缓存注册
计划一. 通过本地配置文件注册
- 批改
appsettings.json
文件,别离配置多级缓存配置以及 Redis 缓存配置
{
// 多级缓存全局配置,非必填
"MultilevelCache": {
"SubscribeKeyPrefix": "masa",// 默认订阅方 key 前缀,用于拼接 channel
"SubscribeKeyType": 3, // 默认订阅方 key 的类型,默认 ValueTypeFullNameAndKey,用于拼接 channel
"CacheEntryOptions": {
"AbsoluteExpirationRelativeToNow": "00:00:30",// 相对过期时长(距以后工夫)"SlidingExpiration": "00:00:50"// 滑动过期时长(距以后工夫)}
},
// Redis 分布式缓存配置
"RedisConfig": {
"Servers": [
{
"Host": "localhost",
"Port": 6379
}
],
"DefaultDatabase": 3
}
}
- 增加多级缓存并应用分布式 Redis 缓存
builder.Services.AddMultilevelCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache();
});
计划二. 通过手动指定配置
builder.Services.AddMultilevelCache(distributedCacheOptions =>
{distributedCacheOptions.UseStackExchangeRedisCache(RedisConfigurationOptions);
});
未配置内存缓存时,默认内存缓存永恒无效
除了上述两种形式以外,多级缓存的内存缓存配置也同样反对选项模式,咱们能够通过 Dcc 或者利用 builder.Services.Configure<MultilevelCacheOptions>(builder.Configuration)
来反对选项模式
批改缓存 Key 映射规定
源码解读
IDistributedCacheClient (分布式缓存客户端)
IDistributedCacheClient
接口提供以下办法来解决分布式缓存
以下办法会依据全局缓存 Key 的规定配置以及传入缓存 Key 的规定配置,检测是否须要格式化缓存 Key,对须要格式化 Key 的操作依照缓存 Key 格式化规定进行解决,具体查看:
Get<T>
、GetAsync<T>
: 依据缓存 Key 返回类型为T
的后果 (如果缓存不存在,则返回 Null)GetList<T>
、GetListAsync<T>
: 依据缓存 Key 汇合返回对应的缓存值的汇合 (针对不存在的缓存 key,其值返回 Null)GetOrSet<T>
、GetOrSetAsync<T>
: 如果在缓存中找到,则返回类型为T
的后果,如果缓存未找到,则执行Setter
,并返回Setter
的后果Set<T>
、SetAsync<T>
: 将指定的缓存 Key 以及缓存值增加到缓存SetList<T>
、SetListAsync<T>
: 将指定的缓存 Key、Value 汇合增加缓存Remove<T>
、RemoveAsync<T>
: 将指定的缓存 Key (缓存 Key 汇合) 从缓存中移除-
Refresh<T>
、RefreshAsync<T>
: 刷新指定的缓存 Key (缓存 Key 汇合) 的生命周期- 实用于未被删除、相对过期工夫没有到,但绝对过期工夫快到的缓存 (缩短滑动过期工夫)
Exists<T>
、ExistsAsync<T>
: 如果在缓存中找到,则返回 true,否则返回 falseGetKeys<T>
、GetKeysAsync<T>
: 依据 key pattern 失去合乎规定的所有缓存 KeyGetByKeyPattern<T>
、GetByKeyPatternAsync<T>
: 依据 key pattern 失去合乎规定的所有缓存 Key、Value 汇合HashIncrementAsync
: 将指定的缓存 Key 的值减少 Value,并返回增长后的后果-
HashDecrementAsync
: 将指定的缓存 Key 的值缩小 Value,并返回缩小后的后果- 反对设置最小的 Value,防止缩小后的值低于设置的最小值,执行失败则返回: -1
KeyExpire<T>
、KeyExpireAsync<T>
: 设置缓存 Key 的生命周期
以下办法不执行缓存 Key 格式化, 应传入缓存残缺 Key:
Remove
、RemoveAsync
: 将指定的缓存 Key (缓存 Key 汇合) 从缓存中移除-
Refresh
、RefreshAsync
: 刷新指定的缓存 Key (缓存 Key 汇合) 的生命周期- 实用于未被删除、相对过期工夫没有到,但绝对过期工夫快到的缓存
Exists
、ExistsAsync
: 如果在缓存中找到,则返回 true,否则返回 false-
GetKeys
、GetKeysAsync
: 依据 key pattern 失去合乎规定的所有缓存 Key- 例: 传入 User*,可失去缓存中以 User 结尾的所有缓存 Key
KeyExpire
、KeyExpireAsync
: 设置缓存 Key 的生命周期
IMultilevelCacheClient (多级缓存客户端)
Get<T>
、GetAsync<T>
: 依据缓存 Key 返回类型为T
的后果 (如果缓存不存在,则返回 Null) (反对监控缓存变更)GetList<T>
、GetListAsync<T>
: 依据缓存 Key 汇合返回对应的缓存值的汇合 (针对不存在的缓存 key,其值返回 Null)GetOrSet<T>
、GetOrSetAsync<T>
: 如果在缓存中找到,则返回类型为T
的后果,如果缓存未找到,则执行Setter
,并返回Setter
的后果Set<T>
、SetAsync<T>
: 将指定的缓存 Key 以及缓存值增加到缓存SetList<T>
、SetListAsync<T>
: 将指定的缓存 Key、Value 汇合增加缓存Remove<T>
、RemoveAsync<T>
: 将指定的缓存 Key (缓存 Key 汇合) 从缓存中移除-
Refresh<T>
、RefreshAsync<T>
: 刷新指定的缓存 Key (缓存 Key 汇合) 的生命周期- 实用于未被删除、相对过期工夫没有到,但绝对过期工夫快到的缓存 (缩短滑动过期工夫)
IDistributedCacheClientFactory (分布式缓存工厂)
- Create: 返回指定 Name 的分布式缓存客户端
IMultilevelCacheClientFactory (多级缓存工厂)
- Create: 返回指定 Name 的多级缓存客户端
如果 Name 为空字符串时,可间接应用
IDistributedCacheClient
或IMultilevelCacheClient
, 默认注册不指定 Name 时,则其 Name 为空字符串,可不通过 Factory 创立
总结
Masa Framework
提供了分布式缓存以及多级缓存的实现,其中有几个优良的性能:
-
多级缓存提供了缓存更新后同步更新内存缓存性能
- 当咱们的服务是多正本时,不用放心会缓存更新后其它正本因为内存缓存未过期,导致获取到过期的缓存数据,大大晋升咱们的用户体验
-
反对滑动过期以及相对过期混合应用
- 防止无用的缓存长时间被长久化,但对于热点数据又能够防止打到 Redis 或者数据库
- 配置反对热更新,配置更新后同步失效,无需重启我的项目
-
缓存 Key 反对格式化,可依据以后缓存值类型与传入缓存 Key 联合造成新的缓存 Key,进步了开发效率以及代码可读性
- 比方获取用户 id 为 1 的数据,可通过
Client.Get<User>("1")
,而无需:Client.Get<User>("User.1")
- 比方获取用户 id 为 1 的数据,可通过
本章源码
Assignment16
https://github.com/zhenlei520…
开源地址
MASA.Framework:https://github.com/masastack/…
如果你对咱们的 MASA Framework 感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们
- WeChat:MasaStackTechOps
- QQ:7424099