干掉RedisHelper,请这样用分布式缓存
前言
我们在项目中使用Redis时通常是写一个单例模式的RedisHelper静态类,暴露一些常用的Get、Set等操作,在需要使用地方直接RedisHelper.StringGet(xx,xx)就可以了,这样虽然简单粗暴地满足我们对Redis的所有操作需要,但是这在Asp.Net Core的项目显得不是那么优雅了。首先你的RedisHelper静态类无法使用Asp.Net Core容器,又如何优雅的通过依赖注入获取IConfiguration中的配置项呢?既然我们使用Asp.Net Core这么优秀的框架,最佳实践当然就是遵循官方建议的开发规范优雅的编写代码。
IDistributedCache
若要使用 SQL Server 分布式缓存,请添加对 Microsoft.Extensions.Caching.SqlServer 包的包引用。
若要使用 Redis 分布式缓存,请添加对 Microsoft.Extensions.Caching.StackExchangeRedis 包的包引用。
若要使用 NCache 分布式缓存,请添加对 NCache.Microsoft.Extensions.Caching.OpenSource 包的包引用。
无论选择哪种实现,应用都将使用
IDistributedCache接口与缓存进行交互。
来看下IDistributedCache这个接口的定义
namespace Microsoft.Extensions.Caching.Distributed;
/// <summary>
/// Represents a distributed cache of serialized values.
/// </summary>
public interface IDistributedCache
{
/// <summary>
/// Gets a value with the given key.
/// </summary>
byte[]? Get(string key);
/// <summary>
/// Gets a value with the given key.
/// </summary>
Task<byte[]?> GetAsync(string key, CancellationToken token = default(CancellationToken));
void Set(string key, byte[] value, DistributedCacheEntryOptions options);
/// <summary>
/// Sets the value with the given key.
/// </summary>
Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken));
/// <summary>
/// Refreshes a value in the cache based on its key, resetting its sliding expiration timeout (if any).
/// </summary>
void Refresh(string key);
/// <summary>
/// Refreshes a value in the cache based on its key, resetting its sliding expiration timeout (if any).
/// </summary>
Task RefreshAsync(string key, CancellationToken token = default(CancellationToken));
/// <summary>
/// Removes the value with the given key.
/// </summary>
void Remove(string key);
/// <summary>
/// Removes the value with the given key.
/// </summary>
Task RemoveAsync(string key, CancellationToken token = default(CancellationToken));
}
IDistributedCache 接口提供以下方法来处理分布式缓存实现中的项:
Get、GetAsync:如果在缓存中找到,则接受字符串键并以 byte[] 数组的形式检索缓存项。Set、SetAsync:使用字符串键将项(作为 byte[] 数组)添加到缓存。Refresh、RefreshAsync:根据键刷新缓存中的项,重置其可调到期超时(如果有)。Remove、RemoveAsync:根据字符串键删除缓存项。
干掉RedisHelper
官方不仅提出了如何最佳实践分布式缓存的使用,还提供了基本的实现库给我们直接用,比如我们在项目中用Redis为我们提供缓存服务:
- 添加引用
Microsoft.Extensions.Caching.StackExchangeRedis - 注册容器
AddStackExchangeRedisCache,并配置参数
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr");
options.InstanceName = "SampleInstance";
});
- 在需要使用
Redis的地方通过构造函数注入IDistributedCache实例调用即可
这样就可以优雅的使用Redis了,更加符合Asp.Net Core的设计风格,养成通过容器注入的方式来调用我们的各种服务,而不是全局使用RedisHelper静态类,通过IOC的方式,结合面向接口开发,能方便的替换我们的实现类,统一由容器提供对象的创建,这种控制反转带来的好处只可意会不可言传,这里就不赘述了。
AddStackExchangeRedisCache到底干了什么
上面已经知道如何优雅的使用我们的Redis了,但是不看下源码就不知道底层实现,总是心里不踏实的。

源码比较好理解的,因为这个Nuget包的源码也就四个类,而上面注册容器的逻辑也比较简单
AddStackExchangeRedisCache主要干的活
// 1.启用Options以使用IOptions
services.AddOptions();
// 2.注入配置自定义配置,可以通过IOptions<T>注入到需要使用该配置的地方
services.Configure(setupAction);
// 3.注入一个单例IDistributedCache的实现类RedisCache
services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCache>());
所以我们在需要用Redis的地方通过构造函数注入IDistributedCache,而它对应的实现就是RedisCache,那看下它的源码。

这里就不细看所有的实现了,重点只需要知道它继承了IDistributedCache就行了,通过AddStackExchangeRedisCache传入的ConnectionString,实现IDistributedCache的Get、Set、Refresh、Remove四个核心的方法,我相信这难不倒你,而它也就是干了这么多事情,只不过它的实现有点巧妙。

通过LUA脚本和HSET数据结构实现,HashKey是我们传入的InstanceName+key,做了一层包装。
源码中还有需要注意的就是,我们要保证Redis连接对象IConnectionMultiplexer的单例,不能重复创建多个实例,这个想必在RedisHelper中也是要保证的,而且是通过lock来实现的。
然而微软不是那么用的,玩了个花样,注意下面的_connectionLock.Wait();:
private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
[MemberNotNull(nameof(_cache), nameof(_connection))]
private void Connect()
{
CheckDisposed();
if (_cache != null)
{
Debug.Assert(_connection != null);
return;
}
_connectionLock.Wait();
try
{
if (_cache == null)
{
if (_options.ConnectionMultiplexerFactory == null)
{
if (_options.ConfigurationOptions is not null)
{
_connection = ConnectionMultiplexer.Connect(_options.ConfigurationOptions);
}
else
{
_connection = ConnectionMultiplexer.Connect(_options.Configuration);
}
}
else
{
_connection = _options.ConnectionMultiplexerFactory().GetAwaiter().GetResult();
}
PrepareConnection();
_cache = _connection.GetDatabase();
}
}
finally
{
_connectionLock.Release();
}
Debug.Assert(_connection != null);
}
通过SemaphoreSlim限制同一时间只能有一个线程能访问_connectionLock.Wait();后面的代码。
学到装逼技巧+1
思考
IDistributedCache只又四中操作:Get、Set、Refresh、Remove,我们表示很希望跟着官方走,但这个接口过于简单,不能满足我的其他需求咋办?
比如我们需要调用 StackExchange.Redis封装的LockTake,LockRelease来实现分布式锁的功能,那该怎么通过注入IDistributedCache调用?
我们可以理解官方上面是给我们做了示范,我们完全可以自己定义一个接口,比如:
public interface IDistributedCachePlus : IDistributedCache
{
bool LockRelease(string key, byte[] value);
bool LockTake(string key, byte[] value, TimeSpan expiry);
}
继承IDistributedCache,对其接口进行增强,然后自己实现实现AddStackExchangeRedisCache的逻辑,我们不用官方给的实现,但是我们山寨官方的思路,实现任意标准的接口,满足我们业务。
services.Add(ServiceDescriptor.Singleton<IDistributedCachePlus, RedisCachePlus>());
在需要使用缓存的地方通过构造函数注入IDistributedCachePlus。
总结
官方提供的IDistributedCache标准及其实现类库,能方便的实现我们对缓存的简单的需求,通过遵循官方的建议,我们干掉了RedisHelper,优雅的实现了分布式Redis缓存的使用,你觉得这样做是不是很优雅呢?
干掉RedisHelper,请这样用分布式缓存的更多相关文章
- 分布式缓存Memcached/memcached/memcache详解及区别
先来解释下标题中的三种写法:首字母大写的Memcached,指的是Memcached服务器,就是独立运行Memcached的后台服务器,用于存储缓存数据的“容器”.memcached和memcache ...
- C# Azure 存储-分布式缓存Redis的新建&配置&查看
1. 介绍 Redis 是一款开源的,基于 BSD 许可的,高级键值 (key-value) 缓存 (cache) 和存储 (store) 系统.由于 Redis 的键包括 string,hash,l ...
- Asp.Net Core 轻松学-正确使用分布式缓存
前言 本来昨天应该更新的,但是由于各种原因,抱歉,让追这个系列的朋友久等了.上一篇文章 在.Net Core 使用缓存和配置依赖策略 讲的是如何使用本地缓存,那么本篇文章就来了解一下如何使用分 ...
- 一个技术汪的开源梦 —— 公共组件缓存之分布式缓存 Redis 实现篇
Redis 安装 & 配置 本测试环境将在 CentOS 7 x64 上安装最新版本的 Redis. 1. 运行以下命令安装 Redis $ wget http://download.redi ...
- ASP.Net MVC4+Memcached+CodeFirst实现分布式缓存
ASP.Net MVC4+Memcached+CodeFirst实现分布式缓存 part 1:给我点时间,允许我感慨一下2016年 正好有时间,总结一下最近使用的一些技术,也算是为2016年画上一个完 ...
- C# Azure 存储-分布式缓存Redis在session中的配置
1. 开始 对于分布式的缓存,平常的session的处理是一个用户对应一台分布式的机器,如果这台机器中途挂机或者不能处理这个用户session的情况发生,则此用户的session会丢失,会发生不可预知 ...
- 企业项目开发--分布式缓存Redis
第九章 企业项目开发--分布式缓存Redis(1) 注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis ...
- EhCache RMI 分布式缓存/缓存集群
EhCache 系统简介 EhCache 是一个纯 Java 的进程内缓存框架,具有快速.精干等特点. EhCache 的主要特性有: 快速.精干 简单: 多种缓存策略: 缓存数据有两级:内存和磁盘, ...
- 第九章 企业项目开发--分布式缓存Redis(1)
注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis 1.1.为什么用分布式缓存(或者说本地缓存存在的问题 ...
随机推荐
- 规范之“用流中的Stream.Of(arr1,arr2)将两个集合合并”
案例:用流中的Stream.Of(arr1,arr2)将两个集合合并 /** * 功能描述: * 两个对象集合添加到一起 * 在用flatMap扁平化改为Stream<User> * 这样 ...
- 定时任务__@Xxl-JOB的使用
概述xxl-job框架 首先我们要知道什么是XXL-JOB? 官方简介:XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并接入多家公司 ...
- 前端进阶(8) - 前端开发需要了解的工具集合:webpack, eslint, prettier, ...
前端开发需要了解的工具集合:webpack, eslint, prettier, ... 前端开发需要了解的一些工具,这些工具能够帮助你在项目开发中事半功倍. 1. nrm: npm registry ...
- 破解浏览器同源政策利器之JSONP
本文是在了解了浏览器的同源规则之后,学习了破解这个规则的一个简单有效的方法->JSONP.主要通过阮一峰老师的博客学习 浏览器的同源规则 有这样一个背景,如果你通过银行的网站进行的取钱的交易,而 ...
- 《头号玩家》AI电影调研报告(四)
五. 现实中对于技术的实现 1.自由变身 电影中,主人公借助"魔幻眼镜"让自己快速变成另一个人的模样,现实中我们一样也能做到! 在今年游戏开发者大会(GDC)上,一个名为" ...
- Kubernetes部署单元-Pod
在 k8s 搞出 pod 概念的时候,其实 docker 官方就已经推出自己的容器编排应用 swarm.这一套服务可以帮助在不同节点上的容器,进行统一的管理,主要针对容器的启停,运维,还有部署,注意我 ...
- Python学习报告及后续学习计划
第一次有学习Python的想法是源于寒假在家的时候,高中同学问我是否学了Python(用于深度学习),当时就到b站收藏了黑马最新的教学视频,但是"收藏过等于我看了",后续就是过完年 ...
- 约定编程与Sping AOP
一.约定编程 Spring AOP是一种约定流程的编程,咱们可以先通过动态代理模式的实现来理解Spring AOP的概念. 代理的逻辑很简单,例如,当你需要采访一名儿童时,首先需要经过他父母的同意,在 ...
- perf性能分析工具使用分享
@ 目录 前言 perf的介绍和安装 perf基本使用 perf list使用,可以列出所有的采样事件 perf stat 概览程序的运行情况 perf top实时显示当前系统的性能统计信息 perf ...
- SIP信令跟踪工具HOMER
概述 HOMER是一款100%开源的针对SIP/VOIP/RTC的抓包工具和监控工具. HOMER是一款强大的.运营商级.可扩展的数据包和事件捕获系统,是基于HEP/EEP协议的VoIP/RTC监控应 ...