ASP.NET Core - 缓存之分布式缓存

分布式缓存是由多个应用服务器共享的缓存,通常作为访问它的应用服务器的外部服务进行维护。 分布式缓存可以提高 ASP.NET Core 应用的性能和可伸缩性,尤其是当应用由云服务或服务器场托管时。

与其他将缓存数据存储在单个应用服务器上的缓存方案相比,分布式缓存具有多个优势。

当分发缓存数据时,数据:

  • 在多个服务器的请求之间保持一致(一致性)。
  • 在进行服务器重启和应用部署后仍然有效。
  • 不使用本地内存。

1. 分布式缓存的使用

.NET Core 框架下对于分布式缓存的使用是基于 IDistributedCache 接口的,通过它进行抽象,统一了分布式缓存的使用方式,它对缓存数据的存取都是基于 byte[] 的。

IDistributedCache 接口提供以下方法来处理分布式缓存实现中的项:

  • Get、GetAsync:如果在缓存中找到,则接受字符串键并以 byte[] 数组的形式检索缓存项。
  • Set、SetAsync:使用字符串键将项(作为 byte[] 数组)添加到缓存。
  • Refresh、RefreshAsync:根据键刷新缓存中的项,重置其可调到期超时(如果有)。
  • Remove、RemoveAsync:根据字符串键删除缓存项。

使用的时候只需要将其通过容器注入到相应的类中即可。

2. 分布式缓存的接入

分布式缓存是基于特定的缓存应用实现的,需要依赖特定的第三方应用,在接入特定的分布式缓存应用时,需要应用对于的 Nuget 包,微软官方提供了基于 SqlServer 、Redis 实现分布式缓存的 Nuget 包,还推荐了基于 Ncache 的方案,除此之外还有像 Memcache 之类的方案,微软虽然没有提供相应的 Nuget 包,但是社区也有相关开源的项目。

这里只讲 .NET Core 下两种分布式缓存的接入和使用,一种是分布式内存缓存,一种是使用得比较广泛的 Redis。其他的在 .NET Core 框架下的使用是差不多的,仅仅只是接入的时候有点区别。当然,Redis 除了作为分布式缓存来使用,还有其他更加丰富的一些功能,后续也会找时间进行一些介绍。

2.1 基于内存的分布式缓存

分布式内存缓存 (AddDistributedMemoryCache) 是框架提供的 IDistributedCache 实现,用于将项存储在内存中,它就在 Microsoft.Extensions.Caching.Memory Nuget 包中。 分布式内存缓存不是真正的分布式缓存。 缓存项由应用实例存储在运行该应用的服务器上。

分布式内存缓存是一个有用的实现:

  • 在开发和测试场景中。

  • 当在生产环境中使用单个服务器并且内存消耗不重要时。 实现分布式内存缓存会抽象缓存的数据存储。 如果需要多个节点或容错,它允许在未来实现真正的分布式缓存解决方案。

当应用在 Program.cs 的开发环境中运行时,我们可以通过以下方式使用分布式缓存,以下示例代码基于 .NET 控制台程序:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;var host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{services.AddDistributedMemoryCache();}).Build();host.Run();

之后还是和内存缓存差不多的例子,演示一下缓存的存取、删除、刷新。

public interface IDistributedCacheService
{Task PrintDateTimeNow();
}
public class DistributedCacheService : IDistributedCacheService
{public const string CacheKey = nameof(DistributedCacheService);private readonly IDistributedCache _distributedCache;public DistributedCacheService(IDistributedCache distributedCache){_distributedCache = distributedCache;}public async Task FreshAsync(){await _distributedCache.RefreshAsync(CacheKey);}public async Task PrintDateTimeNowAsync(){var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");var cacheValue = await _distributedCache.GetAsync(CacheKey);if(cacheValue == null){// 分布式缓存对于缓存值的存取都是基于 byte[],所以各种对象必须先序列化为字符串,之后转换为 byte[] 数组cacheValue = Encoding.UTF8.GetBytes(time);var distributedCacheEntryOption = new DistributedCacheEntryOptions(){//AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(20),AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20),SlidingExpiration = TimeSpan.FromSeconds(3)};// 存在基于字符串的存取扩展方法,内部其实也是通过 Encoding.UTF8 进行了编码// await _distributedCache.SetStringAsync(CacheKey, time, distributedCacheEntryOption);await _distributedCache.SetAsync(CacheKey, cacheValue, distributedCacheEntryOption);}time = Encoding.UTF8.GetString(cacheValue);Console.WriteLine("缓存时间:" + time);Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));}public async Task RemoveAsync(){await _distributedCache.RemoveAsync(CacheKey);}
}

之后,在入口文件添加以下代码,查看控制台结果是否与预想的一致:

using DistributedCacheSample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;var host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{services.AddDistributedMemoryCache();services.AddTransient<IDistributedCacheService, DistributedCacheService>();}).Build();var distributedCache = host.Services.GetRequiredService<IDistributedCacheService>();
// 第一次调用,设置缓存
Console.WriteLine("第一次调用,设置缓存");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(1));
// 未过滑动时间,数据不变
Console.WriteLine("未过滑动时间,数据不变");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(3));
// 已过滑动时间,数据改变
Console.WriteLine("已过滑动时间,数据改变");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(1));
// 未过滑动时间,手动刷新过期时间
Console.WriteLine("未过滑动时间,手动刷新过期时间");
await distributedCache.FreshAsync();
await Task.Delay(TimeSpan.FromSeconds(2));
// 距离上一次调用此方法,已过滑动时间,但由于手动刷新过过期时间,过期时间重新计算,数据不变
Console.WriteLine("距离上一次调用此方法,已过滑动时间,但由于手动刷新过过期时间,过期时间重新计算,数据不变");
await distributedCache.PrintDateTimeNowAsync();
await Task.Delay(TimeSpan.FromSeconds(2));
// 移除缓存
Console.WriteLine("移除缓存");
await distributedCache.RemoveAsync();
// 原有的缓存已移除,调用此方法是重新设置缓存,数据改变
Console.WriteLine("原有的缓存已移除,调用此方法是重新设置缓存,数据改变");
await distributedCache.PrintDateTimeNowAsync();host.Run();

image

结果和预想的是一致的。

2.2 基于 Redis 的分布式缓存

Redis 是一种开源的基于内存的非关系型数据存储,通常用作分布式缓存。在 .NET Core 框架中使用 Redis 实现分布式缓存,需要引用 Microsoft.Extensions.Caching.StackExchangeRedis Nuget 包,包中通过 AddStackExchangeRedisCache 添加 RedisCache 实例来配置缓存实现,该类基于 Redis 实现了 IDistributedCache 接口。

(1) 安装 Redis

这里我在云服务器上通过 Docker 快速安装了 Redis ,映射容器内 Redis 默认端口 6379 到主机端口 6379,并且设置了访问密码为 123456 。

docker run -d --name redis -p 6379:6379 redis --requirepass "123456"

(2) 应用添加依赖包,并且通过配置服务依赖关系

Install-Package Microsoft.Extensions.Caching.StackExchangeRedis

或者通过 VS 的 Nuget 包管理工具进行安装

依赖关系配置如下:

var host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{// services.AddDistributedMemoryCache();services.AddStackExchangeRedisCache(opyions =>{opyions.Configuration = "xxx.xxx.xxx.xxx:6379,password=123456";});}).Build();

这里只需要将原来的分布式内存缓存服务的配置切换为分布式 Redis 缓存的配置即可,其他的什么都不用改,就可以从内存缓存切换到 Redis 分布式缓存了。所以我们在日常工作的应用搭建中推荐使用基于分布式缓存方案,前期或者开发环境中可以使用基于内存的分布式缓存,后面项目的性能要求高了,可以很方便地切换到真正的分布式缓存,只需改动一行代码。

之后基于前面的例子运行应用,可以看到输出的结果是一样的。

image

而在 Redis 上也可以看得到我们缓存上去的数据。

image

这里还有一个要注意的点,理论上使用分布式缓存是能够增强应用的性能和体验性的,但是像 Redis 这样的分布式缓存一般情况下是和应用部署在不同的服务器,每一次缓存的获取会存在一定的网络传输消耗,当缓存的数据量比较大,而且缓存存取频繁的时候,也会有很大的性能消耗。之前在项目中就遇到过这样的问题,由于一个查询功能需要实时进行计算,计算中需要进行循环,而计算依赖于基础数据,这部分的数据是使用缓存的,当初直接使用 Redis 缓存性能并不理想。当然可以说这种方式是有问题的,但是当时由于业务需要,封装的计算方法中需要在应用启动的时候由外部初始化基础数据,为基础数据能够根据前端改动而刷新,所以用了缓存的方式。

下面是一个示例进行内存缓存和 Redis 缓存的对比:

这里利用 BenchmarkDotNet 进行性能测试,需要先对原有的代码进行一下改造,这里调整了一下构造函数,自行实例化相关缓存的对象,之后有三个方法,分别使用 Redis 缓存、内存缓存、内存缓存结合 Redis 缓存,每个方法中模拟业务中的1000次循环,循环中缓存数据进行存取。

点击查看性能测试代码

 

Program.cs 文件中只保留以下代码:

Summary summary = BenchmarkRunner.Run<DistributedCacheService>();
Console.ReadLine();

测试结果如下:

image

可以看到这种情况下使用 Redis 缓存性能是惨不忍睹的,但是另外两种方式就不一样了。

我们在业务中的缓存最终就是第三种方法的方式,结合内存缓存和 Redis 缓存,根本的思路就是在使用时将数据临时保存在本地,减少网络传输的消耗,并且根据实际业务情况控制内存缓存的超时时间以保持数据的一致性。 

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

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

相关文章

如何保证数据传输的安全?

要确保数据传输的安全&#xff0c;您可以采取以下措施&#xff1a; 使用加密协议&#xff1a;使用安全的传输协议&#xff0c;如HTTPS(HTTP over SSL/TLS)或其他安全协议&#xff0c;以保护数据在传输过程中的安全性。加密协议可以有效防止数据被窃听或篡改。 强化身份验证&…

克服多语言语音技术的障碍:五大挑战和创新解决方案

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑器的3D应用场景 介绍 在用西班牙语&#xff08;您的首选语言&#xff09;向语音助手询问某些内容后&#xff0c;您有多少次不得不暂停&#xff0c;然后用语音助手理解的语言&#xff08;可能是英语&#xff09;重述…

Python爬虫(十二)_XPath与lxml类库

Python学习指南 有同学说&#xff0c;我正则用的不好&#xff0c;处理HTML文档很累&#xff0c;有没有其他的方法&#xff1f; 有&#xff01;那就是XPath,我们可以用先将HTML文档转换成XML文档&#xff0c;然后用XPath查找HTML节点或元素。 什么是XML XML指可扩展标记语言(E…

前后端分离------后端创建笔记(06)新增接口页面布局

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化

目录 1. 再谈构造 1.1 成员变量的初始化&#xff08;初始化列表&#xff09; 1.2 初始化列表的行为 1.3 explicit关键字 2. 类中的static成员 2.1 静态成员变量 2.2 静态成员函数 3. 友元 3.1 友元函数 3.1 友元类 4. 内部类 5. 匿名对象 6. 对象拷贝时候的编译器优化…

C++入门基础(万字详解!!!)

文章目录 前言1.C关键字2.命名空间3.C的输入输出4.缺省参数4.1 全缺省4.2 半缺省 5.函数重载6. 引用6.1 引用的特性6.2 引用的使用场景6.3 引用和指针 7.内联函数7.1 特性 8.auto关键字8.1 注意事项 9. 基于范围的for循环9.1 使用条件 10.指针控制nullptr10.1 注意事项 11.总结…

并发编程面试题2

并发编程面试题2 一、AQS高频问题&#xff1a; 1.1 AQS是什么&#xff1f; AQS就是一个抽象队列同步器&#xff0c;abstract queued sychronizer&#xff0c;本质就是一个抽象类。 AQS中有一个核心属性state&#xff0c;其次还有一个双向链表以及一个单项链表。 首先state…

8.15黄金能否跌破千九?日内如何稳健布局

近期有哪些消息面影响黄金走势&#xff1f;黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周二&#xff08;8月15日&#xff09;亚洲时段&#xff0c;现货黄金延续低位徘徊&#xff0c;目前交投于1906.01美元/盎司附近&#xff0c;美国财长称耶伦称美国经济处于…

Linux与安卓安全对抗

导读大家都知道安卓是基于Linux内核&#xff0c;而且大家也知道Linux的安全性是公认的&#xff0c;那为什么和Linux有着类似嫡系关系的安卓却一直被人诟病不安全呢&#xff1f;要想说清楚这个问题&#xff0c;我们需要了解一下安卓和Linux到底是什么关系&#xff0c;而且这两个…

4WRZ25E3-220-5X/6A24NZ4/D3M不带位移反馈比例阀放大器

该先导阀是一个由比例电磁铁控制的三通减压阀&#xff0c;它的作用是将一个输入信号转化为一个与其成比例的压力输出信号&#xff0c;可用于所有的4WRZ...和5WRZ...型比例阀的控制。比例电磁铁是可调试&#xff0c;湿式直流电磁铁结构&#xff0c;带中心螺纹&#xff0c;线圈可…

机器学习-特征选择:如何使用递归特征消除算法自动筛选出最优特征?

一、引言 在实际应用中&#xff0c;特征选择作为机器学习和数据挖掘领域的重要环节&#xff0c;对于提高模型性能和减少计算开销具有关键影响。特征选择是从原始特征集中选择最相关和最具区分力的特征子集&#xff0c;以提高模型的泛化能力和可解释性。 特征选择在实践中具有以…

快手商品详情数据API 抓取快手商品价格、销量、库存、sku信息

快手商品详情数据API是用来获取快手商品详情页数据的接口&#xff0c;请求参数为商品ID&#xff0c;这是每个商品唯一性的标识。返回参数有商品标题、商品标题、商品简介、价格、掌柜昵称、库存、宝贝链接、宝贝图片、商品SKU等。 接口名称&#xff1a;item_get 公共参数 名…

【JavaEE进阶】SpringBoot 日志

文章目录 一. 日志有什么用?二. 自定义日志打印1. 日志的使用与打印 三. 日志级别1. 日志级别有什么用?2. 日志级别的分类及使用 四. 日志持久化五. 更简单的日志输出---Lombok1. Lombok的使用2. lombok原理解释2.1 Lombok更多注解说明 一. 日志有什么用? 在Java中&#xf…

SpringSecurity结合电商项目

pom <!--SpringSecurity及JWT依赖配置--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</ artifactId></dependency> <!--Hutool Java工具包--> <dependency>&l…

Vue3组件库

Vue组件库 ViteVue3TypescriptTSX 1、项目搭建 1.1、创建项目&#xff08;yarn&#xff09; D:\WebstromProject>yarn create vite yarn create v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh pa…

idea报错:java: 程序包org.springframework.web.bind.annotation不存在

这个错误通常都是maven仓库的问题&#xff0c;试了网上很多方法&#xff0c;都没有解决&#xff0c;如果大家有遇到这个问题&#xff0c;且试了很多方法之后都没有解决&#xff0c;不妨可以试试我这个方法 先编译一下已经写好的代码&#xff0c;这时候会出现以上报错&#xff…

文本分类实战-NLP

数据集及任务分析 项目主题&#xff1a;新闻的主题分类&#xff0c;10分类任务 一般对于NLP项目来说的话需要进行数据预处理的&#xff0c;但是由于本项目的数据是经过处理过的&#xff0c;所以就不需要进行数据预处理了&#xff0c;但是数据预处理对NLP项目是重中之重的。 TH…

Linux上安装温度监控软件

文章目录 Linux上安装温度监控软件IDRAC设置 Linux上安装温度监控软件 服务器的温度是影响服务器性能重要条件&#xff0c;怎么监控机器的温度呢&#xff0c;这里知道的有两种方式 通过管理界面&#xff0c;查看机器的温度通过机器上安装监监控软件来监控温度 在物理机上怎么…

微软电脑surface键盘无法使用问题解决

昨天下班后&#xff0c;正常关掉电脑&#xff0c;今天来上班发现键盘无法使用了 打人工找到了解决方法 开机->到锁屏页面->使用屏幕键盘输入密码进入电脑 然后右键左下角的win图标 找到设备管理器->键盘 全部右键卸载 再找到设备管理->系统设备 把这个DTX也卸…

腾讯云国际站代充-阿里云ECS怎么一键迁移到腾讯云cvm?

今天主要来介绍一下如何通过阿里云国际ECS控制台一键迁移至腾讯云国际CVM。腾讯云国际站云服务器CVM提供全面广泛的服务内容。无-需-绑-定PayPal&#xff0c;代-充-值腾讯云国际站、阿里云国际站、AWS亚马逊云、GCP谷歌云&#xff0c;官方授权经销商&#xff01;靠谱&#xff0…