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

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

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

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

实现方案采用 Redis 作为缓存的数据托管方案,接口使用微软官方的 IDistributedCache 接口实现。

首选注入 分布式缓存服务

//注册缓存服务 Redis模式
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("redisConnection");
options.InstanceName = "cache";
});

IDistributedCache 的扩展类,我们后面的过期了

using Microsoft.Extensions.Caching.Distributed;

namespace Common
{ /// <summary>
/// 扩展分布式缓存接口,集成常用方法
/// </summary>
public static class IDistributedCacheExtension
{ /// <summary>
/// 删除指定key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool Remove(this IDistributedCache distributedCache, string key)
{
try
{
distributedCache.Remove(key);
return true;
}
catch
{
return false;
}
} /// <summary>
/// 设置object类型的key
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool SetObject(this IDistributedCache distributedCache, string key, object value)
{
try
{
var valueStr = JsonHelper.ObjectToJson(value);
distributedCache.SetString(key, valueStr);
return true;
}
catch
{
return false;
}
} /// <summary>
/// 设置string类型key,包含有效时间
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
public static bool SetString(this IDistributedCache distributedCache, string key, string value, TimeSpan timeOut)
{
try
{
distributedCache.SetString(key, value, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = timeOut });
return true;
}
catch
{
return false;
}
} /// <summary>
/// 设置object类型key,包含有效时间
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="timeOut"></param>
/// <returns></returns>
public static bool SetObject(this IDistributedCache distributedCache, string key, object value, TimeSpan timeOut)
{
try
{
var valueStr = JsonHelper.ObjectToJson(value);
distributedCache.SetString(key, valueStr, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = timeOut });
return true;
}
catch
{
return false;
}
}


/// <summary>
/// 读取 Object 类型的key
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static T GetObject<T>(this IDistributedCache distributedCache, string key)
{
try
{
var valueStr = distributedCache.GetString(key);
var value = JsonHelper.JsonToObject<T>(valueStr);
return value;
}
catch
{
return default!;
}
}


/// <summary>
/// 判断是否存在指定key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool IsContainKey(this IDistributedCache distributedCache, string key)
{
if (string.IsNullOrEmpty(distributedCache.GetString(key)))
{
return false;
}
else
{
return true;
}
} }
}

WebAPI 的缓存过滤器代码如下:

using Common;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json; namespace WebAPI.Filters
{ /// <summary>
/// 缓存过滤器
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CacheDataFilter : Attribute, IActionFilter
{ /// <summary>
/// 缓存时效有效期,单位 秒
/// </summary>
public int TTL { get; set; } /// <summary>
/// 是否使用 Token
/// </summary>
public bool IsUseToken { get; set; } void IActionFilter.OnActionExecuting(ActionExecutingContext context)
{
string key = ""; if (IsUseToken)
{
var token = context.HttpContext.Request.Headers.Where(t => t.Key == "Authorization").Select(t => t.Value).FirstOrDefault(); key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString + "_" + token;
}
else
{
key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString;
} key = "CacheData_" + CryptoHelper.GetMD5(key); try
{
var distributedCache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
var cacheInfo = distributedCache.GetObject<object>(key); if (cacheInfo != null)
{
if (((JsonElement)cacheInfo).ValueKind == JsonValueKind.String)
{
context.Result = new ObjectResult(cacheInfo.ToString());
}
else
{
context.Result = new ObjectResult(cacheInfo);
}
}
}
catch (Exception ex)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<CacheDataFilter>>();
logger.LogError(ex, "缓存模块异常-In");
}
} void IActionFilter.OnActionExecuted(ActionExecutedContext context)
{
try
{
if (context.Result is ObjectResult objectResult && objectResult.Value != null)
{
string key = ""; if (IsUseToken)
{
var token = context.HttpContext.Request.Headers.Where(t => t.Key == "Authorization").Select(t => t.Value).FirstOrDefault(); key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString + "_" + token;
}
else
{
key = context.ActionDescriptor.DisplayName + "_" + context.HttpContext.Request.QueryString;
} key = "CacheData_" + CryptoHelper.GetMD5(key); if (objectResult.Value != null)
{
var distributedCache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
distributedCache.SetObject(key, objectResult.Value, TimeSpan.FromSeconds(TTL));
} }
}
catch (Exception ex)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<CacheDataFilter>>();
logger.LogError(ex, "缓存模块异常-Out");
} }
}
}

缓存过滤器的入参只有两个

  • TTL 缓存有效期以秒为单位
  • IsUseToken 是否使用 Token 区分不同的用户身份,之所以加入这个参数,主要是因为有些接口虽然多个用户请求时的入参一样,但是不同的用户需要返回不同的信息,所以面对这种类型的接口需要将 IsUseToken 设定为 True。

过滤器的使用方法就很简单了,直接附在对应的接口 Action 方法上就可以,如下:

[CacheDataFilter(TTL = 60, IsUseToken = true)]
public DtoUser? GetUserInfo()
{
///省略业务逻辑
}

此处对于 GetUserInfo  接口添加了缓存过滤器,对数据缓存60秒钟,并且针对 不同的Token身份进行了区分,因为这边的逻辑是通过 Token 识别用户身份的,虽然请求没有特别的参数,但是需要为不同用户的请求返回对应的用户信息,并且分别缓存。

至此 .NET WebAPI 采用 IDistributedCache 实现分布式缓存过滤器 Redis 模式 就讲解完了,有任何不明白的,可以在文章下面评论或者私信我,欢迎大家积极的讨论交流,有兴趣的朋友可以关注我目前在维护的一个 .NET 基础框架项目,项目地址如下

.NET WebAPI 采用 IDistributedCache 实现分布式缓存过滤器 Redis 模式的更多相关文章

  1. 分布式缓存技术redis学习系列

    分布式缓存技术redis学习系列(一)--redis简介以及linux上的安装以及操作redis问题整理 分布式缓存技术redis学习系列(二)--详细讲解redis数据结构(内存模型)以及常用命令 ...

  2. Spring Cloud(7):事件驱动(Stream)分布式缓存(Redis)及消息队列(Kafka)

    分布式缓存(Redis)及消息队列(Kafka) 设想一种情况,服务A频繁的调用服务B的数据,但是服务B的数据更新的并不频繁. 实际上,这种情况并不少见,大多数情况,用户的操作更多的是查询.如果我们缓 ...

  3. 分布式缓存技术redis学习系列(一)——redis简介以及linux上的安装

    redis简介 redis是NoSQL(No Only SQL,非关系型数据库)的一种,NoSQL是以Key-Value的形式存储数据.当前主流的分布式缓存技术有redis,memcached,ssd ...

  4. 分布式缓存技术redis学习(一)——redis简介以及linux上的安装

    redis简介 redis是NoSQL(No Only SQL,非关系型数据库)的一种,NoSQL是以Key-Value的形式存储数据.当前主流的分布式缓存技术有redis,memcached,ssd ...

  5. 分布式缓存技术redis系列(一)——redis简介以及linux上的安装

    redis简介 redis是NoSQL(No Only SQL,非关系型数据库)的一种,NoSQL是以Key-Value的形式存储数据.当前主流的分布式缓存技术有redis,memcached,ssd ...

  6. 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

  7. 分布式缓存技术redis学习系列(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

  8. 分布式缓存技术redis学习(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

  9. 分布式缓存技术redis系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

随机推荐

  1. mysql外键创建不成功/失效

    当前mysql版本:SELECT VERSION();结果为:5.5.40. 在复习mysql外键约束时创建表格:stu与grade,目标:grade的id随着student的id级联更新,且限制删除 ...

  2. Linux下删除Mysql

    1.检查mysql服务并关闭相应的进程 [root@bp18425116f0cojd1vnz ~]# ps -ef |grep mysql root 1492 1 0 10:23 ? 00:00:00 ...

  3. Linux YUM 配置源

    Linux Yum 简介 YUM是交互式的以rpm为基础的软件包管理工具.YUM可以根据仓库的元数据信息,去自动的实现系统更新,包括依赖性分析,过期软件包处理.我们也可以利用yum来进行软件安装,删除 ...

  4. LMC7660即-5V产生电路

    LMC7660为小功率极性反转电源转换器,通过LMC7660电路产生-5V电压,其芯片管脚定义如下表所示. LMC7660负电压产生电路如下图所示. 其中6脚当供电电压大于等于5V时该脚必须悬空,当供 ...

  5. 线程池的概念&原理和线程池的代码实现

    线程池:一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源.工作原理:可以用一张图来简洁明了说明: 合理利用线程池能够带来三个好处∶1.降低 ...

  6. Java 17 中的模式匹配与和类型

    Java 17 中的模式匹配与和类型 从 Spring Security 获取用户谈起 使用 Spring Security做用户校验和权限控制时,常常使用和线程绑定的容器来获取当前登录用户. // ...

  7. 如何用车辆历史违章查询API接口进行快速开发

    最近公司项目有一个车辆的历史违章查询显示的小功能,想着如果用现成的API就可以大大提高开发效率,所以在网上的API商店搜索了一番,发现了 APISpace,它里面的车辆历史违章查询API非常符合我的开 ...

  8. 零基础学Java(8)数组

    数组 数组存储相同类型值的序列. 声明数组 数组是一种数据结构,用来存储同一类型值的集合.通过一个整型下标(index,或称索引)可以访问数组中的每一个值.例如,如果a是一个整型数组,a[i]就是数组 ...

  9. 用cmd命令进行磁盘清理(主要是系统盘)

    作用:清理磁盘(主要是系统盘)中不需要的垃圾文件操作方法: 第一步:Windows键+R键 调出cmd命令窗口(窗口图如下:) 第二步:输入框中输入cmd命令,按下Enter键,进入如下图界面: 第三 ...

  10. 加强版:合并果子[NOIP2004]

    题目 链接:https://ac.nowcoder.com/acm/contest/26887/1001 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K, ...