干掉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.为什么用分布式缓存(或者说本地缓存存在的问题 ...
随机推荐
- Demo示例——Bundle打包和加载
Unity游戏里面的场景.模型.图片等资源,是如何管理和加载的? 这就是本文要讲的资源管理方式--bundle打包和加载. 图片 Unity游戏资源管理有很多方式: (1)简单游戏比如demo,可以直 ...
- C++“拷贝构造函数”和“等号重载”有什么区别?
CTypeA(const CTypeB& b)CTypeA& operator=(const CTypeB& b)一直没弄懂这两个有什么区别.只知道,重载了=号,下面复制的时候 ...
- 2. 使用Github
2. 使用Github 2.1 目的 借助github托管项目代码 2.2 基本概念 仓库(Repository) 仓库用来存放项目代码,每个项目对应一个仓库,多个开源项目则有多个仓库 收藏(Star ...
- Flex 布局新旧混合写法详解(兼容微信)
flex 是个非常好用的属性,如果说有什么可以完全代替 float 和 position ,那么肯定是非它莫属了(虽然现在还有很多不支持 flex 的浏览器).然而国内很多浏览器对 flex 的支持都 ...
- python-转换函数使用
输入一个整数和进制,转换成十进制输出 输入格式: 在一行输入整数和进制 输出格式: 在一行十进制输出结果 输入样例: 在这里给出一组输入.例如: 45,8 输出样例: 在这里给出相应的输出.例如: 3 ...
- PAT B1014 福尔摩斯约会
大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm.大侦探很快就明白了,字条上奇 ...
- vue-cropper裁剪上传
效果图: 全部代码: npm install vue-cropper //首先 安装vue-cropper main.js全局引用: import VueCropper from 'vue-cropp ...
- linux 后台运行
一般用 nohup program & 运行状态用cat nohup.txt查询 下面这种关了终端也不会停止 setsid program &>xx.log & 若是不需 ...
- Java---基本程序结构
一个完整的Java程序: /** * 文档注释 * * @author wind8 * */ public class Hello { /** * @param args */ public stat ...
- 01 | 堆、栈、RAII:C++里该如何管理资源?(极客时间笔记)
基本概念 堆,英文是 heap,在内存管理的语境下,指的是动态分配内存的区域.这个堆跟数据结构里的堆不是一回事.这里的内存,被分配之后需要手工释放,否则,就会造成内存泄漏. C++ 标准里一个相关概念 ...