ASP.NET Core 中的缓存机制

作者 : 慕源网 本文共5378个字,预计阅读时间需要14分钟 发布时间: 2021-09-24 共682人阅读

缓存是指存储常用数据的过程,以便可以更快地为将来的任何请求提供这些数据。因此,我们将最常用的数据复制到临时存储中,以便在将来来自客户端的调用中可以更快地访问它。如果我们试着用一个简单的例子来解释,让 User-1 请求一些数据,服务器需要 12-15 秒来获取数据。在获取时,我们会将获取的数据并行复制到任何临时存储中。所以现在,当 User-2 请求相同的数据时,这次我们将简单地从缓存中提供他的数据,并且响应只需要 1-2 秒,因为我们已经将响应存储在我们的缓存中。

有两个与缓存一起使用的重要术语,缓存命中缓存未命中。当可以在缓存中找到数据时发生缓存命中,当在缓存中找不到数据时发生缓存未命中。

缓存显着提高了应用程序的性能,降低了生成内容的复杂性。将应用程序设计为从不直接依赖于缓存内存是很重要的。应用程序应该只缓存不经常更改的数据,并且只有在缓存数据可用时才使用缓存数据。

ASP.NET Core 具有许多缓存功能。但其中的两种主要类型是,

  • 内存缓存
  • 分布式缓存

内存缓存

一个内存高速缓存存储在托管应用程序的单一服务器的内存。基本上,数据缓存在应用程序中。这是显着提高应用程序性能的最简单方法。

内存缓存的主要优点是它比分布式缓存快得多,因为它避免了通过网络进行通信,并且适用于小型应用程序。主要缺点是在云中部署时保持缓存的一致性。

使用 ASP.NET Core 实现内存缓存

首先创建一个 ASP.NET Core Web API 应用程序。

现在在 Startup.cs 文件中添加以下行。这将为我们的应用程序添加一个非分布式内存缓存实现。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    //Rest of the code
}

现在让我们创建一个新的控制器“EmployeeController”。在这个控制器中,我们将实现我们的缓存。

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    private readonly IMemoryCache _memoryCache;
    private readonly ApplicationContext _context;
    public EmployeeController(IMemoryCache memoryCache, ApplicationContext context)
    {
        _memoryCache = memoryCache;
        _context = context;
    }

    [HttpGet]
    public async Task<IActionResult> GetAllEmployee()
    {
        var cacheKey = "employeeList";
        //checks if cache entries exists
        if(!_memoryCache.TryGetValue(cacheKey, out List<Employee> employeeList))
        {
            //calling the server
            employeeList = await _context.Employees.ToListAsync();

            //setting up cache options
            var cacheExpiryOptions = new MemoryCacheEntryOptions
            {
                AbsoluteExpiration = DateTime.Now.AddSeconds(50),
                Priority = CacheItemPriority.High,
                SlidingExpiration = TimeSpan.FromSeconds(20)
            };
            //setting cache entries
            _memoryCache.Set(cacheKey, employeeList, cacheExpiryOptions);
        }
        return Ok(employeeList);
    }
}

这是一个非常简单的实现。我们只是检查是否有任何缓存值可用于特定缓存键。如果存在,它将从缓存中提供数据,如果不存在,我们将调用我们的服务并将数据保存在缓存中。

解释

第 9 行:将 ImemoryCache 注入构造函数

第 16 行:创建缓存键。众所周知,数据将保存为键值对。

第 18 行:检查缓存值是否可用于特定键。

第 24 行:设置缓存。MemoryCacheEntryOptions用于定义缓存的关键属性。其中一些属性是:

1. Priority – Priority 定义了在缓存中保留缓存条目的优先级。默认值设置为正常。

2. Sliding Expiration –一个特定的时间跨度,如果没有人使用,缓存将在该时间段内到期。由于我们将滑动过期时间设置为 20 秒,因此这意味着在缓存进入后,如果 20 秒内没有客户端请求,缓存将过期。

3. Absolute Expiration –指缓存条目的实际过期时间,不考虑滑动过期时间。在我们的代码中,我们将绝对过期时间设置为 50 秒。所以这意味着缓存肯定会每 50 秒过期一次。

现在让我们观察在实现内存缓存后我们的应用程序的性能提升。

为此,运行应用程序并使用 Postman 向 Web API 发送获取请求。因此,我们第一次向 API 发送请求大约需要 2061 毫秒。

因此,当我们第一次调用 API 时,它直接从数据库中获取数据,然后我们将数据并行存储到缓存中。

现在,如果我们这次为相同的数据请求相同的端点,则只需要 20 毫秒。

所以这是一个非常惊人的改进。就我而言,数据集很小。如果有大量关于这种情况的数据,它将大大改善我们的服务。

分布式缓存

分布式缓存是一种可以由一个或多个应用程序共享的缓存,它作为一种所有服务器都可以访问的外部服务进行维护。所以分布式缓存是应用程序外部的。

分布式缓存的主要优点是数据在多个服务器中保持一致,因为服务器位于应用程序外部,任何应用程序的任何故障都不会影响缓存服务器。

在这里,我们将尝试使用 Redis 实现分布式缓存。

Redis是一种开源(BSD 许可)、内存中数据结构存储,用作数据库缓存和消息代理。它是非常快速的基于键值的数据库,甚至是 NoSQL 数据库。所以Redis是实现高可用缓存的绝佳选择。

在 Docker 中设置 Redis

第1步

从 docker hub 拉取 docker Redis 镜像。

docker pull redis

第2步

通过将 Redis 端口映射到我们的本地系统端口来运行 Redis 镜像。

docker run --name myrediscache -p 5003:379 -d redis

第 3 步

启动容器。

docker start myrediscache

现在我们的 Redis 已经设置好了,让我们开始使用 ASP.NET Core 应用程序实现分布式缓存。

使用 ASP.NET Core 实现分布式缓存(Redis)

创建一个 ASP.NET Core Web API 项目并使用 Nuget 包管理器安装以下库。

由于我们已经添加了所需的包,现在在Startup.cs文件中注册服务。

public void ConfigureServices(IServiceCollection services) {
    //Rest of the code
    services.AddStackExchangeRedisCache(options => {
        options.Configuration = Configuration.GetConnectionString("Redis");
        options.InstanceName = "localRedis_";
    });
}

在这里,我们设置了“options.InstanceName”,它只是作为我们在 redis 服务器上的键名的前缀。前任。如果我们在 redis 服务器中设置缓存名称employeelist,它将类似于localRedis_employeelist。

我们将在 appsettings.json 中提供 Redis 的配置相关设置。

{
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "Redis": "localhost:5003",
    "DefaultConnection": "Data Source=.;Initial Catalog=BuildingDataDB;Integrated Security=True"
  }
}

创建一个帮助器类“DistributedCacheExtensions”,我们将在其中从 Redis 缓存获取和设置值。

public static class DistributedCacheExtension {
    public static async Task SetRecordAsync < T > (this IDistributedCache cache, string recodeId, T data, TimeSpan ? absoluteExpireTime = null, TimeSpan ? slidingExpirationTime = null) {
        var options = new DistributedCacheEntryOptions();
        options.AbsoluteExpirationRelativeToNow = absoluteExpireTime ?? TimeSpan.FromSeconds(60);
        options.SlidingExpiration = slidingExpirationTime;
        var jsonData = JsonSerializer.Serialize(data);
        await cache.SetStringAsync(recodeId, jsonData, options);
    }
    public static async Task < T > GetRecordAsync < T > (this IDistributedCache cache, string recordId) {
        var jsonData = await cache.GetStringAsync(recordId);
        if (jsonData is null) {
            return default (T);
        }
        return JsonSerializer.Deserialize < T > (jsonData);
    }
}

这里的代码是不言自明的。在“ SetRecodeAsync ”方法中,我们将数据保存到Redis Cache。在这里,我们使用AbsoluteExpirationRelativeToNow SlidingExpiration(第 12 行和第 13 行)配置了 IDistributedCache 服务器,我们已经在内存缓存部分讨论了这些术语。

在“ GetRecordAsync ”中,我们根据某些 recodeKey 获取缓存值。

现在我们将创建一个名为“StudentController”的控制器,

public class StudentController: ControllerBase {
    private readonly ApplicationContext _context = null;
    private readonly IDistributedCache _cache;
    public StudentController(ApplicationContext context, IDistributedCache cache) {
            _context = context;
            _cache = cache;
        }
        [HttpGet]
    public async Task < ActionResult < List < Student >>> Get() {
        var cacheKey = "GET_ALL_STUDENTS";
        List < Student > students = new List < Student > ();
        var data = await _cache.GetRecordAsync < List < Student >> (cacheKey);
        if (data is null) {
            Thread.Sleep(10000);
            data = _context.Student.ToList();
            await _cache.SetRecordAsync(cacheKey, data);
        }
        return data;
    }
}

解释

第 5 行:在我们的构造函数中注入 IDistributeCache。

第 15 行:创建缓存键

第 18 行:尝试从 Redis 缓存服务器获取 。如果在缓存服务器上找到数据,它将为客户端提供缓存数据。

第 20 行:检查缓存数据是否可用。如果数据没有被缓存,那么它将从数据库或其他服务中获取数据。因此,为了模拟它,我们故意使用Thread.Sleep()方法设置了一些延迟。

所以这很简单。

现在让我们运行应用程序。

所以第一次,我们调用student控制器的Get方法加载数据需要12秒左右。对于第一次运行,它没有在缓存中找到数据,因此它会转到数据库并获取数据,并将获取的数据并行保存到 Redis 缓存服务器。

但在第二次运行中,它会在 28 毫秒内获取数据。因为用户第二次请求同一个数据应用,发现数据已经缓存在Redis服务器中,所以将缓存的数据提供给用户。

这是我们应用程序的超级优化,速度极快。

因此,我们的缓存介绍之旅到此结束。在这里,我尽量保持示例简单,并讨论了它的基本概念。希望你会发现它有帮助。

快乐编码!


慕源网 » ASP.NET Core 中的缓存机制

常见问题FAQ

程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!

发表评论

开通VIP 享更多特权,建议使用QQ登录