干掉RedisHelper,请这样用分布式缓存

前言

我们在项目中使用Redis时通常是写一个单例模式的RedisHelper静态类,暴露一些常用的GetSet等操作,在需要使用地方直接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 接口提供以下方法来处理分布式缓存实现中的项:

  • GetGetAsync:如果在缓存中找到,则接受字符串键并以 byte[] 数组的形式检索缓存项。

  • SetSetAsync:使用字符串键将项(作为 byte[] 数组)添加到缓存。

  • RefreshRefreshAsync:根据键刷新缓存中的项,重置其可调到期超时(如果有)。

  • RemoveRemoveAsync:根据字符串键删除缓存项。

干掉RedisHelper

官方不仅提出了如何最佳实践分布式缓存的使用,还提供了基本的实现库给我们直接用,比如我们在项目中用Redis为我们提供缓存服务:

  1. 添加引用Microsoft.Extensions.Caching.StackExchangeRedis

  2. 注册容器AddStackExchangeRedisCache,并配置参数

builder.Services.AddStackExchangeRedisCache(options =>{options.Configuration = builder.Configuration.GetConnectionString("MyRedisConStr");options.InstanceName = "SampleInstance";});
  1. 在需要使用Redis的地方通过构造函数注入IDistributedCache实例调用即可

这样就可以优雅的使用Redis了,更加符合Asp.Net Core的设计风格,养成通过容器注入的方式来调用我们的各种服务,而不是全局使用RedisHelper静态类,通过IOC的方式,结合面向接口开发,能方便的替换我们的实现类,统一由容器提供对象的创建,这种控制反转带来的好处只可意会不可言传,这里就不赘述了。

AddStackExchangeRedisCache到底干了什么

上面已经知道如何优雅的使用我们的Redis了,但是不看下源码就不知道底层实现,总是心里不踏实的。

2d1bdfad374199e8c98804ca597edb72.png

源码比较好理解的,因为这个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,那看下它的源码。

80279ad788105f0e569bfeb58ae716b3.png这里就不细看所有的实现了,重点只需要知道它继承了IDistributedCache就行了,通过AddStackExchangeRedisCache传入的ConnectionString,实现IDistributedCacheGetSetRefreshRemove四个核心的方法,我相信这难不倒你,而它也就是干了这么多事情,只不过它的实现有点巧妙。17dfb5d6274ccef127b2e653c852e3fb.png通过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只有四个操作:GetSetRefreshRemove,我们表示很希望跟着官方走,但这个接口过于简单,不能满足我的其他需求咋办?
比如我们需要调用 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缓存的使用,你觉得这样做是不是很优雅呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287252.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4个常用的awk统计命令

1、求和cat data|awk {sum$1} END {print "Sum ", sum}2、求平均cat data|awk {sum$1} END {print "Average ", sum/NR}3、求最大值cat data|awk BEGIN {max 0} {if ($1>max) max$1 fi} END {print "Max", max}4、求最小值&#xff08;min…

《看聊天记录都学不会C#?太菜了吧》(2)C#那么简单我为何之前还学C语言?

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

Android之java.lang.ClassCastException: *****cannot be cast to*******

1 问题 在写BaseMultiItemQuickAdapter适配器的时候&#xff0c;更具不同类型构建不同的实体对象&#xff0c;提示错误如下 2 原因 Overrideprotected void convert(BaseViewHolder helper, MultiItemEntity item) {switch (helper.getItemViewType()) {case AppCleanHead.app…

php函数的初步使用

通过调用函数&#xff0c;实现打印半金字塔、全金字塔、空心金字塔、菱形、空心菱形 调用例程 huaTuMain.php被调用函数 huaTu.php转载于:https://www.cnblogs.com/seaBiscuit0922/p/5613141.html

ORACLE 11G DATA GUARD主从切换

当oracle data guard中如主库出现当机时,需要将从库切换为主库,以下从实验环境模拟主从库切换操作:1.查看主从库角色192.168.88.120:192.168.88.119:2.将主库(88.120)切换为从库alter database commit to switchover to physical standby with sessionB shutdown;shutdown im…

【Pix4d精品教程】大疆精灵4A无人机航空摄影测量外业数据采集完整操作流程

本项目我们采用大疆精灵4A无人机进行航空摄影测量外业数据采集,主要内容包括:航摄准备、像控点布设、航线规划、飞行、数据传输等。 Pix4dmapper摄影测量内业数据处理、空三及4D产品生产部分,请参照文章:《Pix4dmapper摄影测量内业数据处理完整流程》 目录 一、航摄准备 …

oracle进程结构中完成更新,Oracle 进程结构

Oracle进程结构进程是动态创建的&#xff0c;完成任务后就消亡&#xff1b;而程序是静态的实体&#xff0c;程序是可以复制、编辑的。进程强调的是执行过程&#xff0c;而程序仅仅是指令的有序集合&#xff1b;进程在内存中&#xff0c;程序在外存中。ORACLE分为用户进程和ORAC…

国外设计师眼中的原型工具Mockplus

2019独角兽企业重金招聘Python工程师标准>>> 今天&#xff0c;我们评述一款新型设计工具&#xff0c;Mockplus&#xff0c;专为设计师而打造的创新性原型设计工具。Mockplus适合于各种应用软件的制作&#xff0c;无论是手机应用&#xff0c;网页应用&#xff0c;亦或…

《看聊天记录都学不会C#?太菜了吧》(1)从今天开始我是一个游戏开发者

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

Android之让EditText不被键盘挡住

1 问题 点击EditText的时候&#xff0c;键盘挡住了&#xff0c;不好输入内容 2 解决办法 在AndroidManifest.xml相应的activity里面添加 android:windowSoftInputMode"adjustPan|stateHidden"

如何定制.NET6.0的日志记录

在本章中&#xff0c;也就是整个系列的第一部分将介绍如何定制日志记录(系列内容查阅《玩转ASP.NET 6.0框架-序言》)。默认日志记录仅写入控制台或调试窗口&#xff0c;这在大多数情况下都很好&#xff0c;但有时需要写入到文件或数据库&#xff0c;或者&#xff0c;您可能希望…

FreeMarker标签介绍

转自&#xff1a;http://www.blogjava.net/kxbin/articles/366505.html FreeMarker标签使用 一、FreeMarker模板文件主要有4个部分组成 1、文本&#xff0c;直接输出的部分 2、注释&#xff0c;即<#--...-->格式不会输出 3、插值&#xff08;Interpolation&#xff09…

利用Erdas监督分类方法提取城镇用地信息完整实验操作步骤

打开erdas,分波段加载landsat TM 影像信息,并转成img图像格式文件。 1.打开erdas,点击viewer打开新窗口,点击打开图标,选择要加载的一个波段的tif图像,点击OK。 2.点击主窗口File——Save——Top Layer As…,在跳出的Save窗

linux命令 su和sudo,Linux中sudo和su的区别

介绍&#xff1a;主要对su和sudo的功能&#xff0c;区别以及使用进行介绍。两者之间的区别su用于用户之间的切换&#xff1b;sudo用于普通用户可以使用root权限来执行指定命令&#xff1b;命令介绍1. susu简介su用于用户之间的切换。但是前面的用户依然保持在登录状态。这种切换…

通过Xshell上传下载文件

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff09;安装模块 yum install lrzsz 2&#xff09;使用命令 rz #上传文件到Linux sz hello.txt #从Linux下载文件hello.txt 转载于:https://my.oschina.net/u/2351685/blog/519941

Centos 安装配置 vsftpd

为什么80%的码农都做不了架构师&#xff1f;>>> 一、安装vsftpd 执行 yum -y install vsftpd (rpm -qa|grep vsftpd 可查看是否已安装vsftpd, 默认配置文件在/etc/vsftpd/vsftpd/conf) 二、创建虚拟用户 选择在根目录或者用户目录下创建ftp文件夹&#xff1a;mkdir…

《看聊天记录都学不会Python到游戏实战?太菜了吧》(6)不会这个知识点代码越写越难

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

Android之各个手机版本WiFi热点的创建

1 问题 Android手机创建热点,有很多兼容性问题,网上有很多例子,但是不够全,而且高版本反射很多奔溃,我这里再总结下。 2 版本兼容和需要的权限已经热点IP地址说明 1)AndroidMainifest.xml里面需要添加的权限 <uses-permission android:name="android.permissio…

你还在用GIF?那就out了

前言今天介绍一篇使用json格式在wpf中播放动画效果&#xff1b;正文话说在上古&#xff08;1987&#xff09;时代&#xff0c;Gif因其体积小成像相对清晰和非常强的兼容性&#xff0c;而大受欢迎;Gif也因为当时的技术限制导致很多缺陷 这包括对电脑的内存和性能占用非常大;同时…

【摄影测量原理】第一章:摄影测量学基础

目 录 第一节 摄影测量学的定义、任务 第二节 摄影测量与遥感的分类 第三节 摄影测量与遥感的发展历程 第四节 遥感主要应用领域及3S技术 第一节 摄影测量学的定义、任务 先来看一下例子&#xff1a; 思考&#xff1a; 1、摄影测量去常规测量的相同点与不同点&#…