.Net Core缓存组件(Redis)源码解析
上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储。在分析源码之前,先学几个关于Redis操作的命令。
一、Redis命令
Redis所有的命令在http://doc.redisfans.com/上有详细介绍。下面介绍几个常用的关于Hash类型的命令。
HSET:用于添加缓存
用法:HSET key field value 。
返回值:如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。

HMSET:用于同时添加多个
用法:HMSET key [field value field1 value1 ...]
返回值:如果命令执行成功,返回 OK 。

HGET:获取字段值
用法:HGET key field
返回值:给定域的值。
当给定域不存在或是给定 key 不存在时,返回 nil
例如:HGET user Name (注意 Redis区分大小写)

HMGET:获取多个字段的值
用法:HMGET key [field1,field2]
返回值:一个包含多个给定域的关联值的表,表值的排列顺序和给定域参数的请求顺序一样。
例如:HMGET user Name Age

EXPIRE:设置缓存的过期时间
用法:EXPIRE key seconds
返回值:设置成功返回 1 。

二、在.Net Core中使用Redis组件
首先在Startup类中添加Redis缓存功能。配置的Option中设置的InstanceName的值会作为key的一部分。比如设置的InstanceName为test,代码中设置一个缓存key为user,存储到Redis中的实际key为testuser。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDistributedRedisCache(option =>
{
option.Configuration = "121.41.55.55:6379";//连接字符串
option.InstanceName = "test";
});
}
然后在需要使用的地方注入IDistributedCache。如下面所示:
public class ValuesController : Controller
{
private readonly IDistributedCache redisCache;
public ValuesController(IDistributedCache redisCache)
{
this.redisCache = redisCache;
}
[HttpGet]
public IEnumerable<string> Get()
{
redisCache.SetAsync("key1", Encoding.UTF8.GetBytes("value1"), new DistributedCacheEntryOptions()
{
AbsoluteExpiration = DateTime.Now.AddSeconds()//设置过期时间,时间一到缓存立刻就被移除了
}); redisCache.SetString("key2", "value2");//没有设置缓存过期时间,表示是永久缓存 return new string[] { "value1", "value2" };
}
}
三、源码解析
源码在https://github.com/aspnet/Caching,Redis的源码相对简单,主要是因为很多都直接使用的StackExchange.Redis的API。
RedisCacheOptions类:主要是Redis配置相关。
Configuration:设置Redis配置,如连接字符串、超时时间等,最终被装换为StackExchange.Redis中的ConfigurationOptions
InstanceName:实例名称。会和代码中设置的key拼接成为Redis中的key。
RedisCacheServiceCollectionExtensions类:跟服务注入相关。
就一个方法AddDistributedRedisCache,依赖注入IDistributedCache的实例。
public static IServiceCollection AddDistributedRedisCache(this IServiceCollection services, Action<RedisCacheOptions> setupAction)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}
services.AddOptions();
services.Configure(setupAction);
services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCache>());//注入一个单例
return services;
}
RedisCache类:最主要的类,缓存操作相关的类。
其中插入、获取数据的方法比较重要。
3.1 Set方法,插入数据。
public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
{
//省略一些逻辑判断
Connect();
var creationTime = DateTimeOffset.UtcNow;
//对于一个缓存可以设置为绝对过期时间,相对于现在时间的过期时间和滑动过期时间三种(上一篇文章有例子),其实前两种时间类型可以相互转换。
//下面这一步就是 如果设置了绝对过期时间或者相对于现在时间的过期时间,装换为绝对过期时间
var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);
//调用了StackExchange.Redis的API 插入缓存
var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key },//这里的key是实例名称+key=Redis中的key,当然我们在查找缓存的时候,并不需要我们手动拼接,只需要传我们复制的key,不需要实例名称
new RedisValue[]
{
absoluteExpiration?.Ticks ?? NotPresent,
options.SlidingExpiration?.Ticks ?? NotPresent,
//如果对于一个缓存同时设置了绝对过期时间和滑动过期时间,则取即将到期的时间,也就是最小的那个时间。
GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent,
value
});
}
上面的添加缓存中,使用了脚本插入。
private const string SetScript = (@"
redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4])//设置key、绝对过期时间、滑动过期时间、和value的值
if ARGV[3] ~= '-1' then
redis.call('EXPIRE', KEYS[1], ARGV[3])//设置缓存的时间
end
return 1");
如果absexp和sldexp都没有设置值,默认为-1,表示永不过期,缓存时间就是从设置的绝对过期时间和滑动过期时间中取,当时间到了,Redis自动删除过期缓存,这一点和MemoryCache不一样,MemoryCahe是在对缓存操作的时候,会扫描整个缓存删除,存在很大的延时,而Redis采用下面三种策略清理过期的key:
- 被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
- 主动删除:由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key
- 当前已用内存超过maxmemory限定时,触发主动清理策略
这就保证了过期缓存的及时清理。关于Redis清理过期key的策略可以看这篇文章。
当插入一条Hash类型数据时,打开RedisManager会看到下面这样,absexp:绝对过期时间,sldexp:滑动过期时间,data:就是我们代码中设置的value。

3.2 Get方法中,实现的主要获取功能调用了下面代码。
internal static class RedisExtensions
{
private const string HmGetScript = (@"return redis.call('HMGET', KEYS[1], unpack(ARGV))");//通过脚本HMGET命令获取key的值
//Get方法中调用此方法,memebers为固定值 data,也就是获取字段data的值
internal static RedisValue[] HashMemberGet(this IDatabase cache, string key, params string[] members)
{
var result = cache.ScriptEvaluate(
HmGetScript,
new RedisKey[] { key },
GetRedisMembers(members));
return (RedisValue[])result;
} internal static async Task<RedisValue[]> HashMemberGetAsync(
this IDatabase cache,
string key,
params string[] members)
{
var result = await cache.ScriptEvaluateAsync(
HmGetScript,
new RedisKey[] { key },
GetRedisMembers(members)); // TODO: Error checking?
return (RedisValue[])result;
} private static RedisValue[] GetRedisMembers(params string[] members)
{
var redisMembers = new RedisValue[members.Length];
for (int i = ; i < members.Length; i++)
{
redisMembers[i] = (RedisValue)members[i];
}
return redisMembers;
}
}
Remove方法就直接调用了StackExchange的API,这里就不做解释。
相比MemoryCache的代码,Redis代码相对简单,主要是微软的开发人员"偷工减料"吧(我自己感觉),很多重要的方法,比如Redis连接、添加、设置过期时间、都调用了StackExchange的API,没有实现自己的链接池等等。更像是对StackExchangeAPI中的Hash类型的再次封装。
.Net Core缓存组件(Redis)源码解析的更多相关文章
- .Net Core缓存组件(MemoryCache)源码解析
一.介绍 由于CPU从内存中读取数据的速度比从磁盘读取快几个数量级,并且存在内存中,减小了数据库访问的压力,所以缓存几乎每个项目都会用到.一般常用的有MemoryCache.Redis.MemoryC ...
- Redis源码解析:15Resis主从复制之从节点流程
Redis的主从复制功能,可以实现Redis实例的高可用,避免单个Redis 服务器的单点故障,并且可以实现负载均衡. 一:主从复制过程 Redis的复制功能分为同步(sync)和命令传播(comma ...
- Django 之 admin组件使用&源码解析
admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 settings.py 中的 INSTALLED ...
- .Net Core中的配置文件源码解析
一.配置简述 之前在.Net Framework平台开发时,一般配置文件都是xml格式的Web.config,而需要配置其他格式的文件就需要自己去读取内容,加载配置了..而Net Core支持从命令行 ...
- Redis源码解析之跳跃表(三)
我们再来学习如何从跳跃表中查询数据,跳跃表本质上是一个链表,但它允许我们像数组一样定位某个索引区间内的节点,并且与数组不同的是,跳跃表允许我们将头节点L0层的前驱节点(即跳跃表分值最小的节点)zsl- ...
- Redis源码解析:13Redis中的事件驱动机制
Redis中,处理网络IO时,采用的是事件驱动机制.但它没有使用libevent或者libev这样的库,而是自己实现了一个非常简单明了的事件驱动库ae_event,主要代码仅仅400行左右. 没有选择 ...
- 26. SpringBoot 初识缓存及 SimpleCacheConfiguration源码解析
1.引入一下starter: web.cache.Mybatis.MySQL @MapperScan("com.everjiankang.cache.dao") @SpringBo ...
- Netty高性能组件——FastThreadLocal源码解析(细微处见真章)
1. 前言 netty自行封装了FastThreadLocal以替换jdk提供的ThreadLocal,结合封装的FastThreadLocalThread,在多线程环境下的变量提高了ThreadLo ...
- Redis源码解析:26集群(二)键的分配与迁移
Redis集群通过分片的方式来保存数据库中的键值对:一个集群中,每个键都通过哈希函数映射到一个槽位,整个集群共分16384个槽位,集群中每个主节点负责其中的一部分槽位. 当数据库中的16384个槽位都 ...
随机推荐
- SQLite 安装
Windows 平台安装 下载地址:https://www.sqlite.org/download.html 下载预编译的安装包 将下载的安装包=解压到一个文件夹,有三个重要文件: sqlite3.e ...
- boost asio 学习(五) 错误处理
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=6 5. Erro ...
- 转载-对js中new、prototype的理解
说明:本篇文章是搜集了数家之言,综合的结果,应向数家致谢 说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: <script type="text/java ...
- Android 软件退出系统方法重写
Android 软件退出系统按键方法重写 //针对Andriod软件退出系统按键方法重写 ;//key down time @Override public boolean onKeyDown(int ...
- mac开发常用工具和插件记录
1.alfred 是 Mac 系统上一款专注于效率提升的著名应用,它能帮你快速打开网页.快速进行自定义搜索.查看剪贴板历史.快速查询单词等等.Alfred 提供的功能虽然很多,但目的只有一个 —— 那 ...
- uva1673(后缀自动机)
后缀自动机还是只会打板子,已经知道它是个什么东西了,但还是和它的构造联系不起来..先背板子吧. 后缀自动机有一个很好的特性就是可以涵盖所有不重复的子串,我们利用这一点在它上面dp就行了: 代码参考:h ...
- 《mysql必知必会》学习_第22章_20180809_欢
第22章:使用视图,视图是虚拟的表,以表形式呈现的是你查询的结果.并不是说在数据库里面真的存在这个表,但是是真的存在这些数据. select cust_name,cust_contact from c ...
- navicat连接linux系统中mysql-错误:10038
输入 命令 netstat -anp(查看所有的进程和端口使用情况) (注:Local Address一列中: 0.0.0.0 表示监听本地所有ip地址,其他电脑是可以访问的,并且修改ip不受影响. ...
- Java学习笔记34(集合框架八:综合案例:模拟斗地主的洗牌发牌)
规则: 1.54张扑克牌,有花色 2.顺序打乱,一人一张依次发牌,一人17张,留三张作为底牌 3.看牌:按大小王2A....43的序排列打印 示例: package demo; import java ...
- maven配置多仓库的方法
刚接触maven就是在公司里配置好的,所以一直以来使用都没毛病,所以一直没有去动这些固有的东西. 但是,后来把公司的电脑拿回家之后,发现有的东西就搞不起来了.原因也看一下就明白了,因为在公司的时候用的 ...