干掉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,一经查实,立即删除!

相关文章

《看聊天记录都学不会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…

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

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

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

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

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

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

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

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

通过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;之后实战课程也将会逐步更新。 若…

你还在用GIF?那就out了

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

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

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

云堆栈三大服务模式解析

如果准备使用云计算或者要在自己的IT环境中部署云计算&#xff0c;首先要确定合适的服务模式。这就需要我们对每种服务模式含义和特点都有所了解&#xff0c;很清楚从中能够获得哪些有益东西。正所谓“知己知彼百战不殆”&#xff0c;我们要对自己想要得到什么非常了解&#xf…

Excel数据生成SQL insert语句

CONCATENATE("insert into teacher(ID, NAME, ADDRESS) values (",A1,",",B1,",",C1,");")转载于:https://blog.51cto.com/kxtand/1704951

《看聊天记录都学不会Python到游戏实战?太菜了吧》(5)用前朝的剑斩今朝的官

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

【摄影测量原理】第二章:单幅影像解析基础

本章提纲: 第一节 空中摄影基本知识第二节 中心投影与正射投影第三节 共线方程第四节 航摄像片的像点位移第五节 单幅影像解析基础第一节 空中摄影基本知识 2.1.1 航空摄影机 1. 光学航空摄影机 <

Blazor Hybrid / MAUI 简介和实战

1. BlazorBlazor 是一个使用 .NET 生成交互式客户端 Web UI 的框架&#xff1a;使用 C# 代替 JavaScript 来创建信息丰富的交互式 UI。共享使用 .NET 编写的服务器端和客户端应用逻辑。将 UI 呈现为 HTML 和 CSS&#xff0c;以支持众多浏览器&#xff0c;其中包括移动浏览器。与…

SpringMVC总结帖

SpringMVC是基于MVC设计理念的一款优秀的Web框架&#xff0c;是目前最流行的MVC框架之一&#xff0c;SpringMVC通过一套注解&#xff0c;让POPJ成为处理请求的控制器&#xff0c;而无需实现任何接口&#xff0c;然后使用实现接口的控制器也完全没问题&#xff1b;支持REST风格的…

基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面

系列文章基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客&#xff1f;基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目基于.NetCore开发博客项目 StarBlog - (3) 模型设计基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入基于.N…