1. 缓存

缓存指的是在软件应用运行过程中,将一些数据生成副本直接进行存取,而不是从原始源(数据库,业务逻辑计算等)读取数据,减少生成内容所需的工作,从而显著提高应用的性能和可伸缩性,使用好缓存技术,有利于提高我们提升用户体验性。

对于缓存的使用有以下一些注意点:

  • 缓存最适用于不常更改且生成成本很高的数据。
  • 代码应始终具有回退选项,以提取数据,而不依赖于可用的缓存值。

    我们应该以从不依赖于缓存数据的方式编写和测试应用。缓存是会失效的,我们在进行应用开发时应该考虑到缓存失效的情况,提供缓存失效时按照正常逻辑获取相关数据的方式。
  • 缓存使用短缺资源:内存。 我们应该限制缓存增长:
    • 不要将外部输入插入到缓存中。 例如,不建议使用用户提供的任意输入作为缓存键,因为输入可能会消耗不可预测的内存量。
    • 使用过期限制缓存增长。
    • 应当限制缓存的大小,避免缓存过度增长

软件开发中对缓存的使用一般有两种情况,一种是内存缓存,一种是分布式缓存。

2. NET Core 的内存缓存

内存缓存是最简单的一种缓存方式,就是使用应用所在的服务器的内存来保存一些数据副本,利用内存读写比磁盘、网络请求快的特点来提供应用性能。内存缓存一般应用于单机应用,一旦应用重启,内存缓存中的数据就会丢失。

如果是在服务器场(多个服务器)中运行的应用使用内存缓存,应确保在使用内存中缓存时会话是粘滞的。 粘滞会话可确保来自客户端的请求都转到同一服务器。

2. 1 内存缓存启用

.NET Core 框架下对于内存缓存的使用是通过 IMemoryCache ,可以通过将其注册到容器中,之后在需要的地方注入使用。对于大多数应用,在 Program.cs 中调用许多其他 Add{Service} 方法可以启用 IMemoryCache,例如 AddMvc、AddControllersWithViews、AddRazorPages、AddMvcCore().AddRazorViewEngine 等 。

如果应用中没有使用到上述的这些方法,我们也可以自行引入 Microsoft.Extensions.Caching.Memory Nuget 包,通过 AddMemoryCache 方法启动内存缓存。为了方便演示,以下示例使用 .NET 6 控制台应用。

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

2. 2 内存缓存基本用法

内存缓存的使用,只需要将 IMemoryCache 服务注入到类中进行使用即可。我们可以通过 TryGetValue 方法尝试从缓存中获取数据,通过 Set 方法向缓存中添加数据。

public interface ICacheService
{
public void PrintDateTimeNow();
} public class CacheService : ICacheService
{
public const string CacheKey = "CacheTime";
private readonly IMemoryCache _cache;
public CacheService(IMemoryCache memoryCache)
{
_cache = memoryCache;
} public void PrintDateTimeNow()
{
var time = DateTime.Now;
if(!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
{
cacheValue = time;
_cache.Set(CacheKey, cacheValue);
}
time = cacheValue; Console.WriteLine("缓存时间:" + time.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
}
} using CacheSample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddMemoryCache();
services.AddTransient<ICacheService, CacheService>();
})
.Build(); var service = host.Services.GetRequiredService<ICacheService>();
service.PrintDateTimeNow(); Task.Delay(TimeSpan.FromSeconds(2)).Wait(); service.PrintDateTimeNow(); host.Run();

通过控制台打印结果,可以看到,当前时间已经改变,但是缓存的时间是之前的数据。

缓存系统将缓存项存储为键值对,内存缓存中键和值都可以是任意类型,不过一般情况下我们会将字符串作为键。之后会讲到的分布式缓存中,则要求值必须是 byte[] 类型。

除此之外,还可以通过 GetOrCreate 或 GetOrCreateAsync 将获取和添加的操作结合。

public class CacheService : ICacheService
{
public const string CacheKey = "CacheTime";
private readonly IMemoryCache _cache;
public CacheService(IMemoryCache memoryCache)
{
_cache = memoryCache;
} public void PrintDateTimeNow()
{
//var time = DateTime.Now;
//if(!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
//{
// cacheValue = time;
// _cache.Set(CacheKey, cacheValue);
//}
//time = cacheValue; var time = _cache.GetOrCreate(CacheKey, cacheEntry =>
{
return DateTime.Now;
}); Console.WriteLine("缓存时间:" + time.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine("当前时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
}
}

除了 TryGetValue 方法外,如果你确定缓存中一定存在相应的数据,还可以通过 Get 方法获取数据,Get 方法支持泛型,可以直接进行类型转换,但是如果缓存中不存在该缓存项,则会返回对应类型的默认值。

var timeCache = _cache.Get<DateTime>(CacheKey);![image]

2.3 缓存过期设置

一般情况下,我们会对缓存数据设置过期时间,一个是为将一些长期未被访问的缓存条目移除,避免缓存过度增长,一方面是为了更新数据,避免长时间的数据副本和源数据不一致。

.NET Core 下内存缓存的过期时间设置可以通过以下的方式:

(1) 通过 Set 方法设置

var time = DateTime.Now;
if (!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
{
cacheValue = time;
// 设置绝对过期时间
// 两种实现的功能是一样的,只是时间设置的方式不同而已
// 传入的是 AbsoluteExpirationRelativeToNow, 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
_cache.Set(CacheKey, cacheValue, TimeSpan.FromSeconds(2));
// 传入的是 AbsoluteExpiration,绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
// _cache.Set(CacheKey, cacheValue, DateTimeOffset.Now.AddSeconds(2));
}
time = cacheValue;

调整一下入口文件的代码,如下:

var service = host.Services.GetRequiredService<ICacheService>();
service.PrintDateTimeNow(); Task.Delay(TimeSpan.FromSeconds(1)).Wait();
service.PrintDateTimeNow(); Task.Delay(TimeSpan.FromSeconds(2)).Wait();
service.PrintDateTimeNow();

可以看到,在第二次输出的时候,缓存没过期,时间是不变的,第三次的时候缓存过期了,时间改变了。

对于缓存过期时间的设置,除了绝对过期时间,还有缓动过期时间。滑动过期时间是指,如果在规定的过期时间内缓存有被再一次调用,过期时间就会重新更新,从头开始计算,每次被调用都会重新开始。

Set 方法没有直接的参数设置滑动过期时间,只能通过 MemoryCacheEntryOptions 对象设置,当然相对过期时间等其他配置也可以通过该对象设置。

var memoryCacheEntryOption = new MemoryCacheEntryOptions();
// 滑动过期时间是一个相对时间
memoryCacheEntryOption.SlidingExpiration = TimeSpan.FromSeconds(3);
_cache.Set(CacheKey, cacheValue, memoryCacheEntryOption);

可以看到,缓存时间一直没有变,因为虽然三次输出时间加起来超过了三秒,但是三次输出之间的间隔都没有超过3秒,而每调用一次缓存都会刷新超时时间,所以缓存一直没有过期。

(2) 通过 CreateEntry 方法设置

var time = DateTime.Now;
if (!_cache.TryGetValue(CacheKey, out DateTime cacheValue))
{
cacheValue = time;
var entry = _cache.CreateEntry(CacheKey);
// 设置绝对过期时间
// 两种实现的功能是一样的,只是时间设置的方式不同而已
// 传入的是 AbsoluteExpirationRelativeToNow, 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(2);
// 传入的是 AbsoluteExpiration,绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
entry.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2);
entry.Value = cacheValue;
}
time = cacheValue;

(3) 通过 GetOrCreate 或 GetOrCreateAsync 方法设置

var time = _cache.GetOrCreate(CacheKey, cacheEntry =>
{
// 两种实现的功能是一样的,只是时间设置的方式不同而已
// 相对当前的绝对过期时间,传入时间差,会根据当前时间算出绝对过期时间
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3);
// 绝对过期时间,传入一个DateTimeOffset对象,需要明确的指定具体的时间
// cacheEntry.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2);
// 滑动过期时间是一个相对时间
cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
return DateTime.Now;
});

GetOrCreateGetOrCreateAsync 方法的回调参数其实就是实现了 ICacheEntry 接口的对象。

这里同时设置了绝对过期时间和滑动过期时间,对于一个缓存项,仅具有滑动过期时间的缓存项集有永不过期的风险。 如果在滑动过期间隔内重复访问缓存项,则该项永远不会过期。 将滑动过期与绝对过期相结合,以确保项目过期。 绝对过期时间设置一个上限,即在滑动过期间隔内未请求该项时,仍允许该项提前过期的时间。 如果经过了可调到期间隔或绝对到期时间,则会从缓存中逐出项。

可以看到输出结果如下:

第三次调用的时候,时间改变了,这是因为输入滑动过期时间一直在更新,但是绝对过期时间超过了,所以缓存失效了。

这一篇就先到这里,后面还有内容,但是考虑到如果全塞在一篇里面的话,这篇文章就太长了,大家的阅读体验会不大好,所以就拆成两篇了。

参考文章:

ASP.NET Core 中的内存中缓存

ASP.NET Core 系列:

目录:ASP.NET Core 系列总结

上一篇:ASP.NET Core - 选项系统之源码介绍

ASP.NET Core - 缓存之内存缓存(上)的更多相关文章

  1. asp.net core webapi Session 内存缓存

    Startup.cs文件中的ConfigureServices方法配置: #region Session内存缓存 services.Configure<CookiePolicyOptions&g ...

  2. ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

    .NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...

  3. .NET Core应用中使用分布式缓存及内存缓存

    .NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...

  4. ASP.NET Core中使用Cache缓存

    ASP.NET Core中使用Cache缓存 缓存介绍: 通过减少生成内容所需的工作,缓存可以显著提高应用的性能和可伸缩性. 缓存对不经常更改的数据效果最佳. 缓存生成的数据副本的返回速度可以比从原始 ...

  5. ASP.NET Core中的Http缓存

    ASP.NET Core中的Http缓存 Http响应缓存可减少客户端或代理对web服务器发出的请求数.响应缓存还减少了web服务器生成响应所需的工作量.响应缓存由Http请求中的header控制. ...

  6. 将asp.net core站点发布到IIS上遇到的问题

    今天第一次将整个 asp.net core 站点发布到 IIS 上,以前都是发布到 Linux 服务器上. 开始使用 dotnet publish -c release 命令发布,用浏览器访问站点时出 ...

  7. 细说ASP.NET Core静态文件的缓存方式

    一.前言 我们在优化Web服务的时候,对于静态的资源文件,通常都是通过客户端缓存.服务器缓存.CDN缓存,这三种方式来缓解客户端对于Web服务器的连接请求压力的. 本文指在这三个方面,在ASP.NET ...

  8. ASP.NET Core教程:ASP.NET Core中使用Redis缓存

    参考网址:https://www.cnblogs.com/dotnet261010/p/12033624.html 一.前言 我们这里以StackExchange.Redis为例,讲解如何在ASP.N ...

  9. Asp.Net Core 自定义设置Http缓存处理

    一.使用中间件 拦截请求自定义输出文件 输出前自定义指定响应头 public class OuterImgMiddleware { public static string RootPath { ge ...

  10. 图片_ _图片缓存之内存缓存技术LruCache,软引用

    每当碰到一些大图片的时候,我们如果不对图片进行处理就会报OOM异常,这个问题曾经让我觉得很烦恼,后来终于得到了解决,那么现在就让我和大家一起分享一下吧.这篇博文要讲的图片缓存机制,我接触到的有两钟,一 ...

随机推荐

  1. 圣诞树代码_HTML

    这个冬天给TA栽不一样的圣诞树 直接上效果 <!DOCTYPE html> <html lang="en" > <head> <meta ...

  2. linux基础命令4

    用户和组群账户管理 用户的 角色是通过UID(用户ID号)来标识的,每个用户的UID都是不同的. 在Linux系统中有三大类用户,分别是root 用户.系统用户和普通用户. root用户UID为0.r ...

  3. 工控小工具 snmp 、opc ua 、modbus 、tcp、bacnet 开发环境Net6.0

    下载地址 https://files.cnblogs.com/files/blogs/745639/net6.0-windows.rar?t=1674114312

  4. CSS 语法-熟悉样式规则

    CSS 规则: CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 样式规则: 以内嵌式样式表为例: (1)所有的css代码都必须书写在<head>标签内部的一对<sty ...

  5. 面向对象ooDay9

    精华笔记: 多态:多种形态 同一个对象被造型为不同的类型时,有不同的功能-------所有对象都是多态的(明天总结详细讲) 对象的多态:水.我.你...... 同一类型的引用在指向不同的对象时,有不同 ...

  6. 十大经典排序之快速排序(C++实现)

    快速排序 通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小.之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的. 思路: (1)选择基准:从数列中挑出一个 ...

  7. ES5及ES6的新增特性

    介绍 es表示ECMASCript ,他是从es3,es5,es6,es5是2009.12月发布的,es6是2015.6月发布的.vue2完全支持es5的(vue3完全支持es6的),react完全支 ...

  8. B树、B+树详解

    B树.B+树详解   B树 前言 首先,为什么要总结B树.B+树的知识呢?最近在学习数据库索引调优相关知识,数据库系统普遍采用B-/+Tree作为索引结构(例如mysql的InnoDB引擎使用的B+树 ...

  9. [Leetcode 701]二叉搜索树BST中插入元素

    题目 BST二叉搜索树中插入元素 二叉搜索树:左边<root<右边 https://leetcode.com/problems/insert-into-a-binary-search-tr ...

  10. Code UTF-8 Console GB2312 Linux 中文乱码

    Linux 系统方法:  LD_LIBRARY_PATH=. ./userdemo | iconv -f GB2312 -t utf8 Shell 方法  Shell 编码 改成GB2312 // 编 ...