干掉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.为什么用分布式缓存(或者说本地缓存存在的问题 ...
随机推荐
- DateFormat类,利用SimpleDateFormat解决系统时间初始(格式化/解析)问题
目标: java.text.DateFormat 是日期/时间格式化子类的抽象类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换. 格式化 ...
- Anaconda 怎么安装cv2
Anaconda run python程序的时候,如果有import cv2, 但是遇到报错的时候, 可以考虑在anaconda 中安装opencv, 安装过程非常简单. 什么是opencv , op ...
- 6_比例积分控制器_PI控制
- 浅谈JavaScript原型与原型链
对于很多前端开发者而言,JavaScript的原型实在是很让人头疼,所以我这边就整理了一下自己对应原型的一点理解,分享给大家,供交流使用 原型 说起原型,那就不得不说prototype.__proto ...
- javascript 理解和使用回调函数
在javascript中,function是内置的类对象,也就是说它是一种类型的对象,可以和其他String.Array.Number.Objec类的对象一样用于内置对象的管理.因为function实 ...
- HTML5打造原生应用——Ionic框架简介与Ionic Hello World
试了试用Ionic框架打造了两个应用,然后在Google Play上架了. 程序语言答人 教你设计物联网 更有意思的是这是在一周的业余时间内完成的三个应用中的两个,接着让我们看看这个框架如何实现高效地 ...
- CCF201909-2小明种苹果(续)
解题思路:解题思路很简答,就是用数组将数据存起来然后再进行统计,具体思路就见代码注释,记录这道题的是为了警示自己好好审题啊...... 审题有问题,写题火葬场啊.......以为每棵树就疏一次果,把D ...
- IDEA中 mybatis-config、applicationContext.xml、log4j.properties、SpringMVC等文件没有图标标识符号,不是一个xml文件
1. 举例说明 mybatis-config.xml文件不显示图标,识别不出该xml文件 2. 解决办法 1)先点击 File -> Settings-,然后贴入下面代码 (2) 具体操作如下图 ...
- java集合(arraylist详解)
一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类 ...
- 【.NET 6】使用EF Core 访问Oracle+Mysql+PostgreSQL并进行简单增改操作与性能比较
前言 唠嗑一下.都在说去O或者开源,但是对于数据库选型来说,很多人却存在着误区.例如,去O,狭义上讲,是去Oracle数据库.但是从广义上来说,是去Oracle公司产品或者具有漂亮国垄断地位和需要商 ...