系列文章目录

链接: 【ASP.NET Core】REST与RESTful详解,从理论到实现
链接: 【ASP.NET Core】深入理解Controller的工作机制
链接: 【ASP.NET Core】内存缓存(MemoryCache)原理、应用及常见问题解析


文章目录

  • 系列文章目录
  • 前言
  • 一、Redis
    • 1.1 Redis简介
    • 1.2 常用数据结构
    • 1.3 Redis的持久化
      • 1.3.1 RDB
      • 1.3.2 AOF
    • 1.4 常用应用场景
      • 1.4.1 缓存
      • 1.4.2 计数器
      • 1.4.2 订阅发布
  • 二、ASP.NET Core中应用Redis【使用IDistributedCache接口】
    • 2.1 安装依赖
    • 2.2 Program.cs 注册
    • 2.3 IDistributedCache
    • 2.4 ASP.NET Core Controller中操作Redis
      • 2.4.1 获取缓存
      • 2.4.2 设置缓存
      • 2.4.3 删除缓存
      • 2.4.4 刷新缓存
    • 2.4.5 完整代码
  • 总结


前言

分布式缓存是将缓存数据存储后供多个外部应用服务器中的服务共享使用。比起内存缓存仅支持本服务使用,分布式缓存扩展多服务器,多应用。故因此得名分布式缓存。本文将介绍ASP.NET Core中如何应用Redis作为分布式缓存服务。


一、Redis

在分享ASP.NET Core中应用Redis作为分布式缓存服务前,先简单介绍一下Redis。

1.1 Redis简介

Redis(Remote Dictionary Server)直译过来就是远程字典服务。作为一款开源的高性能内存数据结构存储系统,支持字符串、哈希表、列表等多种数据结构,支持持久化保存功能。并且由于数据存储在内存中,Redis的读写速度远超传统关系型数据库。

1.2 常用数据结构

  • 字符串(String)
    • 二进制字符串,存储文本、JSON、数字或二进制图片数据等
  • 哈希(Hash)
    • 键值对集合,存储对象结构的数据。
  • 列表(List)
    • 有序的字符串列表,一般用作存储消息队列,或者记录时间线。
  • 集合(Set)
    • 无序且唯一的字符串集合,支持交并差操作。因为集合的特性,方便去重的场景使用。
  • 有序集合(Sorted Set)
    • 类似于普通的集合,但每个成员都关联了一个分数(score),一般用作排行榜

1.3 Redis的持久化

Redis的持久化分为两种。一种是RDB,通过将内存中的数据快照写入磁盘达到数据的保存;另外一种是AOF,Redis 将每个写操作追加到 AOF 文件的末尾,通过日志的方式记录操作。

1.3.1 RDB

RDB,既Redis Database,是一种快照持久化机制。是Redis在某一个规则下将某一时刻的内存数据以二进制形式写入磁盘,生成RDB文件。

RDB的配置项内容在在Redis根目录下的名为redis.windows-service.conf的文件里。找到如下的结构

save 900 1		# 900秒内至少1个key被修改
save 300 10		# 300秒内至少10个key被修改
save 60 10000	# 60秒内至少10000个key被修改stop-writes-on-bgsave-error yes	#当RDB 快照生成过程中发生错误时(如磁盘已满、权限不足)停止接受新的写操作,防止数据不一致(默认值:yes)rdbcompression yes	#对RDB文件中的字符串对象启用 LZF 压缩算法rdbchecksum yes	#在RDB文件末尾添加 CRC64 校验和,用于加载时验证文件完整性dbfilename dump.rdb

RDB是默认开启的,执行快照存储的时候会在根目录下新建dump.rdb文件记录快照。

1.3.2 AOF

AOF,既Append Only File。Redis通过将每个写操作(如 SET、INCR)追加到 AOF 文件的末尾,实现日志的记录。显然这种方式的数据安全性最高。

AOF的配置项内容在在Redis根目录下的名为redis.windows-service.conf的文件里。找到如下的结构

appendonly yesappendfilename "appendonly.aof"

AOF并不是默认开启的。考虑到每一步操作写操作都会记录日志。该生成的日志文件会随着服务的运行变得十分巨大。

1.4 常用应用场景

1.4.1 缓存

将数据库热点数据缓存到 Redis,减少数据库访问压力。Redis存储空值得时候会记录NULL,自动解决缓存穿透得问题。

1.4.2 计数器

Redis中INCR是用于将存储在指定键中的数值递增 1 的命令。如果键不存在,Redis会先将其初始化为 0,然后再执行 INCR 操作。由于指令是原子性的,这就为我们实现一个计数器提供很好的先决条件。以及接口限流等这种需要使用到计算的功能。

1.4.2 订阅发布

当出现新的报警通知之类的,发布消息通知所有订阅的客户端。

二、ASP.NET Core中应用Redis【使用IDistributedCache接口】

在ASP.NET Core中应用Redis还是比较简单的,本文应用StackExchangeRedis这个库来对Redis进行操作。

2.1 安装依赖

通过这个指令按照StackExchangeRedis包

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis

2.2 Program.cs 注册

在Program.cs中,我们通过AddStackExchangeRedisCache这个静态扩展方法注册Redis服务。通过观察AddStackExchangeRedisCache源码,我们发现实际上这个扩展方法往DI容器里注册的是IDistributedCache接口以及实现类RedisCacheImpl。生命周期是singleton。

builder.Services.AddStackExchangeRedisCache(options =>
{options.Configuration = builder.Configuration["Redis:ConnectionStrings"];options.InstanceName = builder.Configuration["Redis:InstanceName"];
});
/// <summary>
/// Adds Redis distributed caching services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <param name="setupAction">An <see cref="Action{RedisCacheOptions}"/> to configure the provided
/// <see cref="RedisCacheOptions"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddStackExchangeRedisCache(this IServiceCollection services, Action<RedisCacheOptions> setupAction)
{ArgumentNullThrowHelper.ThrowIfNull(services);ArgumentNullThrowHelper.ThrowIfNull(setupAction);services.AddOptions();services.Configure(setupAction);services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCacheImpl>());return services;
}

2.3 IDistributedCache

IDistributedCache是ASP.NET Core框架提供的一个接口,用于实现分布式缓存,支持多种缓存提供者。也就是说不仅仅是Redis能够通过这个接口被操作。
这个接口定义很简单,总的来说就四种方法。Get,Set,Refresh,Remove。以及对应的四个异步方法。

/// <summary>
/// Represents a distributed cache of serialized values.
/// </summary>
public interface IDistributedCache
{/// <summary>/// Gets a value with the given key./// </summary>/// <param name="key">A string identifying the requested value.</param>/// <returns>The located value or null.</returns>byte[]? Get(string key);/// <summary>/// Gets a value with the given key./// </summary>/// <param name="key">A string identifying the requested value.</param>/// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the located value or null.</returns>Task<byte[]?> GetAsync(string key, CancellationToken token = default(CancellationToken));/// <summary>/// Sets a value with the given key./// </summary>/// <param name="key">A string identifying the requested value.</param>/// <param name="value">The value to set in the cache.</param>/// <param name="options">The cache options for the value.</param>void Set(string key, byte[] value, DistributedCacheEntryOptions options);/// <summary>/// Sets the value with the given key./// </summary>/// <param name="key">A string identifying the requested value.</param>/// <param name="value">The value to set in the cache.</param>/// <param name="options">The cache options for the value.</param>/// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>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>/// <param name="key">A string identifying the requested value.</param>void Refresh(string key);/// <summary>/// Refreshes a value in the cache based on its key, resetting its sliding expiration timeout (if any)./// </summary>/// <param name="key">A string identifying the requested value.</param>/// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>Task RefreshAsync(string key, CancellationToken token = default(CancellationToken));/// <summary>/// Removes the value with the given key./// </summary>/// <param name="key">A string identifying the requested value.</param>void Remove(string key);/// <summary>/// Removes the value with the given key./// </summary>/// <param name="key">A string identifying the requested value.</param>/// <param name="token">Optional. The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>Task RemoveAsync(string key, CancellationToken token = default(CancellationToken));
}

观察以上代码,我们发现返回的RedisValue类型都是byte[]字节数组。这是因为分布式缓存通常运行在独立的服务,与应用服务器可能使用不同的技术栈。为确保数据能被不同语言或框架正确解析,需要一种通用的数据表示形式。这也是IDistributedCache支持多种缓存提供者的原因。

也就是说实际上从分布式缓存里取到的结果从字节数组需要解析成指定格式的数据,存储的时候也需要序列化成字节数组。这样操作尤其麻烦,好在微软提供了一个名为DistributedCacheExtensions的静态扩展,内部帮我们通过 Encoding.UTF8.GetBytes(value)和Encoding.UTF8.GetString(data, 0, data.Length)的形式将结果集和字符串形成转换,相当于少转了一步。

DistributedCacheExtensions源码片段【namespace Microsoft.Extensions.Caching.Distributed】

public static Task SetStringAsync(this IDistributedCache cache, string key, string value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
{ThrowHelper.ThrowIfNull(key);ThrowHelper.ThrowIfNull(value);return cache.SetAsync(key, Encoding.UTF8.GetBytes(value), options, token);
}public static async Task<string?> GetStringAsync(this IDistributedCache cache, string key, CancellationToken token = default(CancellationToken))
{byte[]? data = await cache.GetAsync(key, token).ConfigureAwait(false);if (data == null){return null;}return Encoding.UTF8.GetString(data, 0, data.Length);
}

2.4 ASP.NET Core Controller中操作Redis

2.4.1 获取缓存

根据key获取值,并且转型

	// 尝试从分布式缓存获取数据var cachedData = await _distributedCache.GetStringAsync(cacheKey);Movie? movie = null;if (!string.IsNullOrEmpty(cachedData)){// 反序列化缓存数据movie = JsonSerializer.Deserialize<Movie>(cachedData);_logger.LogInformation("从缓存中获取了电影数据");}

2.4.2 设置缓存

	// 缓存未命中,从数据源获取movie = await _movieAssert.GetMovieAsync(id);if (movie != null){// 设置缓存选项var cacheOptions = new DistributedCacheEntryOptions{// 同时设置绝对过期和滑动过期AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),SlidingExpiration = TimeSpan.FromMinutes(5)};// 序列化并存储到分布式缓存var serializedData = JsonSerializer.Serialize(movie);await _distributedCache.SetStringAsync(cacheKey, serializedData, cacheOptions);_logger.LogInformation("已将电影数据存入缓存");}

2.4.3 删除缓存

根据key删除缓存

	await _distributedCache.RemoveAsync(cacheKey);

2.4.4 刷新缓存

一般是碰到需要手动续滑动过期时间的场景才会使用。Redis中如果请求了一个被设置了滑动过期时间的缓存,会自动刷新滑动过期时间的。

	await _distributedCache.RefreshAsync(cacheKey);

2.4.5 完整代码

[Route("api/[controller]")]
[ApiController]
public class MovieController : ControllerBase
{private readonly ILogger<MovieController> _logger;private readonly IMovieAssert _movieAssert;private readonly IDistributedCache _distributedCache;public MovieController(ILogger<MovieController> logger, IMovieAssert movieAssert, IDistributedCache distributedCache = null){_logger = logger;_movieAssert = movieAssert;_distributedCache = distributedCache;}[HttpGet("{id}")]public async Task<ActionResult<Movie?>> Movies(int id){_logger.LogDebug("开始获取数据");var cacheKey = $"Movie:{id}";// 尝试从分布式缓存获取数据var cachedData = await _distributedCache.GetStringAsync(cacheKey);Movie? movie = null;if (!string.IsNullOrEmpty(cachedData)){// 反序列化缓存数据movie = JsonSerializer.Deserialize<Movie>(cachedData);_logger.LogInformation("从缓存中获取了电影数据");}else{// 缓存未命中,从数据源获取movie = await _movieAssert.GetMovieAsync(id);if (movie != null){// 设置缓存选项var cacheOptions = new DistributedCacheEntryOptions{// 同时设置绝对过期和滑动过期AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),SlidingExpiration = TimeSpan.FromMinutes(5)};// 序列化并存储到分布式缓存var serializedData = JsonSerializer.Serialize(movie);await _distributedCache.SetStringAsync(cacheKey, serializedData, cacheOptions);_logger.LogInformation("已将电影数据存入缓存");}}if (movie is null){return NotFound("没有数据");}return movie;}
}

总结

本文介绍了 Redis 的基本情况及在ASP.NET Core 中借助IDistributedCache接口使用Redis作为分布式缓存的具体操作。

IDistributedCache作为微软封装的一个通用的分布式缓存接口,只能说应用了Redis的一些基础服务。之后我们会讨论如何通过直接注册ConnectionMultiplexer这种方式获取Redis连接对象,使用Redis的那些高级用法。

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

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

相关文章

5.6 指令流水线 (答案见原书 P267)

5.6 指令流水线 (答案见原书 P267) 01. 下列关于流水CPU基本概念的描述中&#xff0c;正确的是&#xff08; D &#xff09;。 题目原文 下列关于流水CPU基本概念的描述中&#xff0c;正确的是&#xff08; &#xff09;。 A. 流水CPU是以空间并行性为原理构造的处理器 B. 流水…

NIO简单介绍和运用

NIO简单介 NIO 非阻塞IO模型&#xff0c;基于缓冲区(Buffer)读写数据&#xff0c;读写后的数据通过通道(Channel)进行传输&#xff0c;采用选择器(Selector)管理多个通道从而实现高并发。 核心组件&#xff1a;1. Buffer 为一个内存数组作为数据容器&#xff0c;代替传统的Inpu…

LeetCode 658.找到K个最接近的元素

给定一个 排序好 的数组 arr &#xff0c;两个整数 k 和 x &#xff0c;从数组中找到最靠近 x&#xff08;两数之差最小&#xff09;的 k 个数。返回的结果必须要是按升序排好的。 整数 a 比整数 b 更接近 x 需要满足&#xff1a; |a - x| < |b - x| 或者 |a - x| |b - x| …

制作一款打飞机游戏83:炸弹机制

游戏中的炸弹系统&#xff0c;包括以下核心功能&#xff1a;炸弹爆炸效果与动画实现炸弹伤害范围判定机制子弹转化为能量道具的系统炸弹使用时的无敌帧处理各种边界情况的修复与优化技术实现细节1. 炸弹基础系统‌炸弹动画状态机‌&#xff1a; 我们采用三阶段状态机控制炸弹效…

Linux CentOS 虚拟机升级内核至4.x以上版本

1、安装组件 yum install -y wget && yum install -y net-tools yum groupinstall “Development Tools” yum install ncurses-devel bc openssl-devel elfutils-libelf-devel yum install -y ncurses-devel yum install -y elfutils-libelf-devel yum install -y ope…

QT跨平台应用程序开发框架(11)—— Qt系统相关

目录 一&#xff0c;事件 1.1 关于事件 1.2 处理事件 1.3 处理鼠标事件 1.3.1 点击事件 1.3.2 释放事件 1.3.3 双击事件 1.3.4 滚轮事件 1.3.5 注意事项 1.4 处理键盘事件 1.5 定时器事件 1.6 窗口移动和大小改变事件 二&#xff0c;文件操作 2.1 文件操作概述 2.2 QFile 介绍…

sqli-labs通关笔记-第11关 POST字符型注入(单引号闭合 手工注入+脚本注入两种方法)

目录 一、字符型注入 二、limit函数 三、GET方法与POST方法 四、源码分析 1、代码审计 2、SQL注入安全分析 五、渗透实战 1、进入靶场 2、注入点分析 &#xff08;1&#xff09;SQL语句 &#xff08;2&#xff09;万能密码登录 3、手工注入 &#xff08;1&#xf…

网络安全基础作业三

回顾web前端的代码<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用户登录</title><st…

基于单片机的温湿度报警系统设计与实现

摘 要 本项研究对温湿度警报系统的需求进行了详尽分析&#xff0c;并成功研制出一套以单片机为技术核心的温湿度警报系统。该系统由硬件搭建和软件编程两大模块构成。在硬件搭建方面&#xff0c;系统整合了STM32主控芯片、DS18B20温度传感器、湿敏电阻、按键组件、OLED显示屏、…

(八)复习(拆分微服务)

文章目录项目地址一、Ticketing模块拆分1.1 创建web api1. 添加引用2. 添加需要的包和配置3. program.cs4. docker-compose修改项目地址 教程作者&#xff1a;教程地址&#xff1a; 代码仓库地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow一、Ticketing模块拆…

DearMom以“新生儿安全系统”重塑婴儿车价值,揽获CBME双项大奖

7月16日&#xff0c;在刚刚开幕的2025 CBME中国孕婴童展上&#xff0c;备受瞩目的CBME中国孕婴童产业奖正式揭晓。深耕婴儿车品类的专业品牌DearMom&#xff0c;凭借其卓越的创新实力与对新生儿安全出行的深刻洞察&#xff0c;一举摘得重量级奖项——“杰出品牌创新奖”。同时&…

瀚高数据库开启Oracle兼容模块

文章目录环境症状问题原因解决方案环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 症状 不能使用Oracle兼容&#xff1b; 问题原因 在瀚高数据库V45中oracle兼容模块需要单独开启默认是关闭状态。 解决方案 使用sysdba执行修改…

final修饰符不可变的底层

final修饰符的底层原理在 Java 中&#xff0c;final 修饰符的底层实现涉及 编译器优化 和 JVM 字节码层面的约束其核心目标是保证被修饰元素的【不可变性】或 【不可重写 / 继承性】一、final 修饰类&#xff1a;禁止继承的底层约束当一个类被 final 修饰时&#xff0c;例如 St…

如何排查服务器 CPU 飙高

服务器 CPU 飙高&#xff08;CPU 使用率持续超过 80% 甚至接近 100%&#xff09;是典型的性能瓶颈问题&#xff0c;可能由应用逻辑缺陷、资源竞争、外部压力或硬件/系统异常引起。以下是系统化的排查步骤&#xff0c;覆盖从现象确认到根因定位的全流程。​一、确认 CPU 飙高的现…

【DataWhale】快乐学习大模型 | 202507,Task05笔记

前言 今天是Transformer的编码实战阶段&#xff0c;照着示例代码执行一遍吧 embedding self.tok_embeddings nn.Embedding(args.vocab_size, args.dim)把token向量转为embedding矩阵&#xff08;一个token一个embedding向量&#xff09; 位置编码 为了解决“我喜欢你”和…

用ffmpeg 进行视频的拼接

author: hjjdebug date: 2025年 07月 22日 星期二 17:06:02 CST descrip: 用ffmpeg 进行视频的拼接 文章目录1. 指定协议为concat 方式.1.1 协议为concat 模式,会调用 concat_open 函数1.2 当读数据时,会调用concat_read2. 指定file_format 为 concat 方式2.1 调用concat_read_…

HTTP与HTTPS技术细节及TLS密钥交换与证书校验全流程

HTTP与HTTPS技术细节及TLS密钥交换与证书校验全流程 引言 文档目的与范围 核心技术栈概述 本文档的核心技术栈围绕传输层安全协议&#xff08;TLS&#xff09;展开。TLS协议作为安全套接字层&#xff08;SSL&#xff09;的后继标准&#xff0c;是现代网络安全通信的基础&am…

广播分发中心-广播注册流程

广播是怎么注册的呢&#xff1f;阶段组件/数据结构作用描述存储位置/关联关系App进程阶段BroadcastReceiver开发者自定义的广播接收器&#xff0c;实现onReceive方法处理事件。App进程&#xff08;Activity/Service等组件内&#xff09;ReceiverDispatcher将BroadcastReceiver封…

OpenCV计算机视觉实战(16)——图像分割技术

OpenCV计算机视觉实战&#xff08;16&#xff09;——图像分割技术0. 前言1. 分水岭算法1.1 应用场景1.2 实现过程2. GrabCut 交互式分割2.1 应用场景2.2 实现过程3. FloodFill3.1 应用场景3.2 实现过程小结系列链接0. 前言 图像分割是计算机视觉中将像素划分为具有特定语义或…

Coturn打洞服务器

* 概念理解&#xff1a;1. SDP协议&#xff1a;会话描述协议&#xff0c;视频通话的双方通过交换SDP信息进行媒体协商&#xff0c;从而选择使用某一相同的媒体协议进行通信&#xff1b;TLS协议&#xff1a;基于TCP的安全层传输协议DTLS协议&#xff1a;基于UDP的安全层传输协议…