MasaFramework -- 缓存入门与规则配置
概念
什么是缓存,在项目中,为了提高数据的读取速度,我们会对不经常变更但访问频繁的数据做缓存处理,我们常用的缓存有:
- 本地缓存
- 内存缓存:IMemoryCache
- 分布式缓存
- Redis: StackExchange.Redis
功能
目前,MasaFramework为我们提供了以下能力
- IDistributedCacheClient: 分布式缓存
- 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,否则返回falseGetKeys、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/MasaFramework.Practice
开源地址
MASA.Framework:https://github.com/masastack/MASA.Framework
MASA.EShop:https://github.com/masalabs/MASA.EShop
MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor
如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

MasaFramework -- 缓存入门与规则配置的更多相关文章
- MasaFramework -- 缓存入门与设计
概念 什么是缓存,在项目中,为了提高数据的读取速度,我们会对不经常变更但访问频繁的数据做缓存处理,我们常用的缓存有: 本地缓存 内存缓存:IMemoryCache 分布式缓存 Redis: Stack ...
- Varnish缓存服务器的搭建配置手册
Varnish缓存服务器的搭建配置手册 1.Varnish官方环境依赖提示 Installing Varnish Cache is as simple as enabling our package ...
- Memcached缓存入门篇
Asp.Net中使用Couchbase——Memcached缓存入门篇 前言 本文的主要目的就是简单的进行使用Memcached.这是Memchahed的官网http://memcached.org/ ...
- Spring入门4.AOP配置深入
Spring入门4.AOP配置深入 代码下载 链接: http://pan.baidu.com/s/11mYEO 密码: x7wa 前言: 之前学习AOP中的一些概念,包括连接点.切入点(pointc ...
- ehcache缓存入门学习
ehcache缓存入门学习 1,概念 特性 EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. 主要的特性有:1. 快速2 ...
- ASP.NET 5 入门 (2) – 自定义配置
ASP.NET 5 入门 (2) – 自定义配置 ASP.NET 5 理解和入门 建立和开发ASP.NET 5 项目 初步理解ASP.NET5的配置 正如我的第一篇文章ASP.NET 5 (vNext ...
- Debian 入门安装与配置2
Debian 入门安装与配置2 1. C/C++开发必装软件 atp-get install gcc 这个不用说,用来编译C程序 apt-get install g++ 用来编译C++程序 ap ...
- Debian 入门安装与配置1
Debian 入门安装与配置1 最近安装了多个发行版本的Linux,包括Ubuntu.Fedora.Centos和Debian,发现只有Debian在界面和稳定性等综合特性上表现最优,自己也最喜欢,所 ...
- EHCache分布式缓存集群环境配置
EHCache分布式缓存集群环境配置 ehcache提供三种网络连接策略来实现集群,rmi,jgroup还有jms.同时ehcache可以可以实现多播的方式实现集群,也可以手动指定集群主机序列实现集群 ...
随机推荐
- 【原创】Selenium获取请求头、响应头
本文仅供学习交流使用,如侵立删! Selenium获取请求头.响应头 操作环境 win10 . mac Python3.9 selenium.seleniumwire selenium是没有办法直接获 ...
- Python 懂车帝综合口碑数据
本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! Python 懂车帝综合口碑数据 需求 ...
- 万答#19,MySQL可以禁用MyISAM引擎吗?
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. MyISAM的适用场景越来越少了. 随着MySQL 8.0的推出,系统表已经全面采用InnoDB引擎,不再需要MyISAM ...
- 分享俩个js数组比较少用的方法join与from
1:array.join 用于将数组中的所有元素转化为字符串 例: var arr = ["one", "two", "four"]; va ...
- win10下计算文件哈希值的方法
cmd下使用certutil命令 使用方法: certutil -hashfile FILE_NAME ALGORITHM_NAME 支持的加密算法包括:MD2,MD4,MD5,SHA1,SHA256 ...
- HDU2065 “红色病毒”问题 (指数型母函数经典板题)
题面 医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶, ...
- Dapr中国社区活动之 分布式运行时开发者日 (2022.09.03)
自2019年10月首次发布以来,Dapr(Distributed Application Runtime,分布式应用运行时)因其"更稳定"."更可靠".&quo ...
- 跟羽夏学 Ghidra ——工具
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- 若依(RuoYi )权限管理设计
前言 若依权限管理包含两个部分:菜单权限 和 数据权限.菜单权限控制着我们可以执行哪些操作.数据权限控制着我们可以看到哪些数据. 菜单是一个概括性名称,可以细分为目录.菜单和按钮,以若依自身为例: 目 ...
- ProxySQL(11):链式规则( flagIN 和 flagOUT )
文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9350631.html 理解链式规则 在mysql_query_rules表中,有两个特殊字段"fl ...