ABP vNext 缓存使用

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 缓存(cache),原始意义是指访问速度比一般 随机存取存储器(RAM) 快的一种 高速存储器,通常它不像系统主存那样使用 DRAM 技术,而使用昂贵但较快速的 SRAM 技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。

缓存在 App 中的应用

image.png

缓存(cache)工作原理

说到缓存,我们来看下百度百科的介绍:

  1. 缓存(cache),原始意义是指访问速度比一般 随机存取存储器(RAM) 快的一种 高速存储器,通常它不像系统主存那样使用 DRAM 技术,而使用昂贵但较快速的 SRAM 技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。

    • 动态随机存取存储器(Dynamic Random Access Memory,DRAM
    • 静态随机存取存储器(Static Random-Access Memory,SRAM
  2. 目的:提高数据存取速度(缓存性能优化 万金油 )。

在应用系统中通常会使用到缓存(cache)技术,其中应用系统多级缓存的工作原理大概介绍如下:

image.png

说明:在 App 系统中存在多级缓存时,按顺序读取,从一级缓存(Level1 Cache,简称 L1 Cache)逐级往下获取。

项目环境准备

上一篇文章 我们介绍了 .net6 平台的 asp.net core webapi 框架中如何使用 ABP vNext 框架,本篇文章我们继续使用上次创建的 Demo.Abp.WebApplication1 项目,新增如下 NuGet 包文件:

  • Volo.Abp.Caching,本地缓存;
  • Volo.Abp.Caching.StackExchangeRedis,分布式缓存(Redis);
说明:Volo.Abp.Caching.StackExchangeRedis 包已经包含 Volo.Abp.Caching 包。

一、添加 NuGet 包文件

Demo.Abp.WebApplication1 项目中所有的 nuget packages 文件:

image.png

说明:在 上篇文章 中, ABP vNext 的这些 nuget packages 文件还未发布 v6.0.0 正式版,生产环境中推荐使用正式稳定版。关于 asp.net core webapi 项目如何遵循 Module(模块化)改造的具体细节,此处不再详细介绍。

添加完成上图的 nuget packages 后,查看 Demo.Abp.WebApplication1.csproj 完整的工程项目文件,如下所示:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Volo.Abp.AspNetCore" Version="6.0.0" />
    <PackageReference Include="Volo.Abp.Caching" Version="6.0.0" />
    <PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="6.0.0" />
    <PackageReference Include="Volo.Abp.Swashbuckle" Version="6.0.0" />
  </ItemGroup>

</Project>
AI 代码解读

项目结构如下:

image.png

二、改造 ApiController 类及相关文件

此处为了模拟业务操作从数据库获取数据,在 WeatherForecastController 控制器中添加一个获取数据的方法:

// 模拟数据库获取数据
private async Task<WeatherForecast> GetWeatherForecastAsync(Guid guid)
{
   _logger.LogDebug($"{DateTime.Now:G},查询数据库数据...");

   var index = Random.Shared.Next(-2, 3);
   var data = new WeatherForecast
   {
      Id = Guid.NewGuid(), // WeatherForecast 模型新增 Id 字段
      Date = DateTime.Now.AddDays(index),
      TemperatureC = Random.Shared.Next(-20, 55),
      Summary = Summaries[Random.Shared.Next(Summaries.Length)]
   };

   return await Task.FromResult(data);
}
AI 代码解读

WeatherForecast 模型新增 Id 字段。

namespace Demo.Abp.WebApplication1;

public class WeatherForecast
{
    public Guid Id { get; set; }
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string? Summary { get; set; }
}
AI 代码解读
说明:此处仅说明 WeatherForecastController 控制器新增的部分方法,完整的改造代码后面会展示,尽量展示思路步骤。

ABP vNext 缓存使用

接下来我们分别演示在 asp.net core webapi 框架中如何使用 ABP vNext 提供的缓存包文件:

  • Volo.Abp.Caching,本地缓存;
  • Volo.Abp.Caching.StackExchangeRed,分布式缓存(Redis);

一、本地缓存使用

由于Demo.Abp.WebApplication1 项目是使用默认创建的模式(MiniAPI) ,在 Program.cs 中的 DemoWebApiModule 类声明式添加 Module 化的 NuGet 包依赖,如下所示:

  1. 导入命名空间(Namespace);
using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.AspNetCore;
using Volo.Abp.Caching;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
AI 代码解读
  1. DemoWebApiModule 类声明式添加 Module 化的 NuGet 包依赖;
[DependsOn(
    typeof(AbpAspNetCoreModule),
    typeof(AbpCachingModule),
    typeof(AbpSwashbuckleModule)
 )]
public class DemoWebApiModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var services = context.Services;
        services.AddControllers();
        services.AddEndpointsApiExplorer();
        services.AddAbpSwaggerGen(options => {
            options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test DemoWebApiModule API", Version = "v1", Description = "Module 模块化 DemoWebApiModule 自定义类。" });
            options.DocInclusionPredicate((docName, description) => true);
            options.CustomSchemaIds(type => type.FullName);
            options.HideAbpEndpoints(); // 隐藏 ABP vNext 的默认端点
        });
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        if (env.IsDevelopment())
        {
            //app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseAbpSwaggerUI(options => {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "TestAPI");
            });
        }
        else
        {
            app.UseExceptionHandler("/Error");
        }

        //app.UseHttpsRedirection();
        app.UseStaticFiles(); // 不使用静态文件中间件,SwaggerUI将无法渲染UI页面
        app.UseRouting();
        app.UseAuthorization();
        app.UseConfiguredEndpoints(); //代替原来的 app.MapControllers();
    }
}
AI 代码解读
  1. WeatherForecastController 控制器中完整代码如下:
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Caching;
using Microsoft.Extensions.Caching.Distributed;

namespace Demo.Abp.WebApplication1.Controllers;

/// <summary>
/// WeatherForecast
/// </summary>
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;
    private readonly IDistributedCache<WeatherForecast> _cache; // 使用 abp vnext 本地缓存
    // 构造函数 DI 注入本地缓存
    public WeatherForecastController(ILogger<WeatherForecastController> logger, IDistributedCache<WeatherForecast> cache)
    {
        _logger = logger;
        _cache = cache;
    }
    
    // 常规查询数据
    [HttpGet("/GetList")]
    public IEnumerable<WeatherForecast> GetDataFromWeatherForecastAsync()
    {
        _logger.LogDebug($"{DateTime.Now:G},查询数据库数据...");
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Id = Guid.NewGuid(),
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToList();
    }

    // 使用 ABP vNext 本地缓存查询数据
    [HttpGet("/Get")]
    public async Task<WeatherForecast> GetDataFromWeatherForecastAsync(Guid guid) 
    {
        _logger.LogDebug($"{DateTime.Now:G},查询数据使用本地缓存...");
        var data = await _cache.GetOrAddAsync(key: guid.ToString(),
            factory: async () => await GetWeatherForecastAsync(guid),
            optionsFactory: () => new DistributedCacheEntryOptions
            {
                // 添加绝对过期时间,设置 10 秒
                AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10)
            });

        return data;
    }

    // 模拟数据库获取数据
    private async Task<WeatherForecast> GetWeatherForecastAsync(Guid guid)
    {
        _logger.LogDebug($"{DateTime.Now:G},查询数据库数据...");

        var index = Random.Shared.Next(-2, 3);
        var data = new WeatherForecast
        {
            Id = guid,
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        };

        return await Task.FromResult(data); // 此处仅为了例演示异步方法使用,该方法本身没啥实际意义
    }
}
AI 代码解读

1.1 启动项目运行

改造好后,我们习惯性的启动项目运行,验证下是否能成功运行,如下所示:

image.png

WeatherForecast 控制器中有两个对外公开的方法,分别是:

  1. /GetList,常规查询(未使用缓存)多条数据。
  2. /Get,使用缓存查询单条数据。

1.2 测试访问接口【/GetList】

1.2.1、curl

curl -X 'GET' \
  'http://localhost:5220/GetList' \
  -H 'accept: text/plain' \
  -H 'RequestVerificationToken: CfDJ8ODjDXixt1tDuxO1cuT_L2xih256N5ivD-oaqHU-qlOmT5DYo778fWPnN-FN9VwU4isJn1azpl9RMoXcaT_GuqD8ICyhdBtSTzizz-W56shntXTF7Uqp2iLUbKwbpURg_imHSk0sTJbF4uM_DGugscE' \
  -H 'X-Requested-With: XMLHttpRequest'
AI 代码解读

1.2.2、Request URL,浏览器输入

http://localhost:5220/GetList
AI 代码解读

1.2.3、响应数据

  • Response body
[
  {
    "id": "8b72f7bb-4ff2-449b-bc07-bbe175c0ba65",
    "date": "2022-10-09T19:15:11.1299624+08:00",
    "temperatureC": -15,
    "temperatureF": 6,
    "summary": "Chilly"
  },
  {
    "id": "a1606cc5-63ed-4a88-ac83-0d1299b452aa",
    "date": "2022-10-10T19:15:11.130221+08:00",
    "temperatureC": 19,
    "temperatureF": 66,
    "summary": "Freezing"
  },
  {
    "id": "3fe5cb1e-5148-49d2-a540-145a0f174abf",
    "date": "2022-10-11T19:15:11.1302229+08:00",
    "temperatureC": 50,
    "temperatureF": 121,
    "summary": "Mild"
  },
  {
    "id": "0c7d9b88-7925-4439-a037-81cd1fe08173",
    "date": "2022-10-12T19:15:11.1302232+08:00",
    "temperatureC": 32,
    "temperatureF": 89,
    "summary": "Mild"
  },
  {
    "id": "a76cc34b-af1a-421c-9719-742e07029f3a",
    "date": "2022-10-13T19:15:11.1302234+08:00",
    "temperatureC": 53,
    "temperatureF": 127,
    "summary": "Hot"
  }
]
AI 代码解读
  • Response headers
 content-type: application/json; charset=utf-8  
 date: Sat,08 Oct 2022 11:15:11 GMT  
 server: Kestrel  
 transfer-encoding: chunked 
AI 代码解读

1.3 测试访问接口【/Get】

1.3.1、curl

curl -X 'GET' \
  'http://localhost:5220/Get?guid=a76cc34b-af1a-421c-9719-742e07029f3a' \
  -H 'accept: text/plain' \
  -H 'RequestVerificationToken: CfDJ8ODjDXixt1tDuxO1cuT_L2xih256N5ivD-oaqHU-qlOmT5DYo778fWPnN-FN9VwU4isJn1azpl9RMoXcaT_GuqD8ICyhdBtSTzizz-W56shntXTF7Uqp2iLUbKwbpURg_imHSk0sTJbF4uM_DGugscE' \
  -H 'X-Requested-With: XMLHttpRequest'
AI 代码解读

1.3.2、Request URL,浏览器输入

http://localhost:5220/GetList
AI 代码解读

1.3.3、响应数据

image.png

缓存默认设置的 10s 失效时间,在该时间段内多次请求访问,都不会直接操作数据库获取数据,而是从本地进程缓存中获取数据,此时说明我们使用到了 ABP vNetx 提供的 Volo.Abp.Caching 包的功能。

二、分布式缓存使用

2.1 ABP vNext 使用 Redis 默认依赖的 NuGet 包

这里我们使用 Redis 作为分布式缓存服务,ABP vNext 默认使用了 StackExchange.Redis 包,连接字符串同 StackExchange.Redis 的写法,如下所示:

127.0.0.1:6379,defaultDatabase=1,password=Abc...123
# 或者
localhost:6379,defaultDatabase=1,password=Abc...123
# 或者
AI 代码解读

2.2 准备 Redis-Server 服务

说明:此处使用的 redis 是 docker 环境的容器服务,开放映射端口 6379,因此可以使用【127.0.0.1:6379】或者【localhost:6379】访问。

image.png

2.3 测试 Redis-Server 可用性

准备好 Redis-Server 后,使用 Redis 客户端 GUI(这里使用 AnotherRedis desktop manager)工具访问连接,测试下该服务是否正常可用,工具连接如下图所示(说明 redis-server 正常可用):

image.png

2.4 修改 appsettings.json 配置文件

接下来在 appsettings.json 文件中添加如下配置:

"Redis": {
    "IsEnabled": true,
    "InstanceName": "StackExchangeRedis", // 可省略
    "Configuration": "localhost:6379,defaultDatabase=1,password=Abc...123" // 未设置密码 password 可以不写
}
AI 代码解读

2.5 自定义模块类添加 Module 化依赖

最后修改自定义模块 DemoWebApiModule 类,修改代码如下:

using Volo.Abp.Caching.StackExchangeRedis; // 导入 abp vnext 提供的 redis 命名空间 

...

[DependsOn(
    typeof(AbpAspNetCoreModule),
    typeof(AbpCachingStackExchangeRedisModule), // 声明 Volo.Abp.Caching.StackExchangeRedis 模块依赖
    typeof(AbpSwashbuckleModule)
 )]
public class DemoWebApiModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        ...
        /* 如果报异常,添加此处配置
        Configure<RedisCacheOptions>(options => {
            options.InstanceName = "StackExchangeRedis"; // 该实例名称可自定义
        });*/
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        ... 
    }
}
AI 代码解读
说明:"..." 省略的代码配置不变,和使用本地缓存一样即可。

2.6 启动项目测试接口

此处的测试环节和使用本地化缓存的测试一样,不再描述,成功访问接口后,我们使用 redis 可视化管理工具 ——AnotherRedis desktop manager 查看【DB1】里面是否存在对应的 keyvalues,如下图所示:

image.png

2.7 使用自定义 RedisModule 模块类

除了使用默认的 AbpCachingStackExchangeRedisModule 模块依赖,还可以自定义MyCachingStackExchangeRedisModule 模块类,该自定义模块类基本 copy 了默认的模块类,只是添加了 options.InstanceName 部分代码,修改如下:

using Microsoft.Extensions.Caching.StackExchangeRedis;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Caching;
using Volo.Abp.Modularity;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Demo.Abp.WebApplication1.Moduls;

[DependsOn(typeof(AbpCachingModule))]
public class MyCachingStackExchangeRedisModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        IConfiguration configuration = context.Services.GetConfiguration();
        string redisEnable = configuration["Redis:IsEnabled"];
        if (!redisEnable.IsNullOrEmpty() && !bool.Parse(redisEnable))
        {
            return;
        }

        context.Services.AddStackExchangeRedisCache(delegate (RedisCacheOptions options)
        {
            // 新增部分
            string redisInstanceName = configuration["Redis:InstanceName"];
            if (!redisInstanceName.IsNullOrEmpty())
            {
                options.InstanceName = redisInstanceName;
            }

            string redisConfiguration = configuration["Redis:Configuration"];
            if (!redisConfiguration.IsNullOrEmpty())
            {
                options.Configuration = redisConfiguration;
            }
        });
        context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache, AbpRedisCache>());
    }
}
AI 代码解读

自定义 Module 模块类创建好后,同样地使用 DependsOn 特性显示声明依赖,代码如下所示:

[DependsOn(
    typeof(AbpAspNetCoreModule),
    typeof(MyCachingStackExchangeRedisModule), // 声明自定义模块依赖
    typeof(AbpSwashbuckleModule)
 )]
AI 代码解读

总结

通过学习 ABP vNext 模块化的缓存(cache)技术,分别提供了 本地化分布式 两种模式的缓存,快速方便的为应用系统接入缓存,局部有效的提升了系统访问性能,当默认提供的分布式缓存服务不满足需求时,还可以自定义扩展分布式缓存模块,对 Module 模块化思想有更近一步的认识。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
打赏
0
0
0
0
1027
分享
相关文章
|
16天前
|
Redis--缓存击穿、缓存穿透、缓存雪崩
缓存击穿、缓存穿透和缓存雪崩是Redis使用过程中可能遇到的常见问题。理解这些问题的成因并采取相应的解决措施,可以有效提升系统的稳定性和性能。在实际应用中,应根据具体场景,选择合适的解决方案,并持续监控和优化缓存策略,以应对不断变化的业务需求。
68 29
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
Redis应用—8.相关的缓存框架
Redis 与 AI:从缓存到智能搜索的融合之路
Redis 已从传统缓存系统发展为强大的 AI 支持平台,其向量数据库功能和 RedisAI 模块为核心,支持高维向量存储、相似性搜索及模型服务。文章探讨了 Redis 在实时数据缓存、语义搜索与会话持久化中的应用场景,并通过代码案例展示了与 Spring Boot 的集成方式。总结来看,Redis 结合 AI 技术,为现代应用提供高效、灵活的解决方案。
Redis缓存设计与性能优化
Redis缓存设计与性能优化涵盖缓存穿透、击穿、雪崩及热点key重建等问题。针对缓存穿透,可采用缓存空对象或布隆过滤器;缓存击穿通过随机设置过期时间避免集中失效;缓存雪崩需确保高可用性并使用限流熔断组件;热点key重建利用互斥锁防止大量线程同时操作。此外,开发规范强调键值设计、命令使用和客户端配置优化,如避免bigkey、合理使用批量操作和连接池管理。系统内核参数如vm.swappiness、vm.overcommit_memory及文件句柄数的优化也至关重要。慢查询日志帮助监控性能瓶颈。
55 9
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
221 85
Redis,分布式缓存演化之路
本文介绍了基于Redis的分布式缓存演化,探讨了分布式锁和缓存一致性问题及其解决方案。首先分析了本地缓存和分布式缓存的区别与优劣,接着深入讲解了分布式远程缓存带来的并发、缓存失效(穿透、雪崩、击穿)等问题及应对策略。文章还详细描述了如何使用Redis实现分布式锁,确保高并发场景下的数据一致性和系统稳定性。最后,通过双写模式和失效模式讨论了缓存一致性问题,并提出了多种解决方案,如引入Canal中间件等。希望这些内容能为读者在设计分布式缓存系统时提供有价值的参考。感谢您的阅读!
141 6
Redis,分布式缓存演化之路
云端问道21期方案教学-应对高并发,利用云数据库 Tair(兼容 Redis®*)缓存实现极速响应
云端问道21期方案教学-应对高并发,利用云数据库 Tair(兼容 Redis®*)缓存实现极速响应
云端问道21期实操教学-应对高并发,利用云数据库 Tair(兼容 Redis®)缓存实现极速响应
本文介绍了如何通过云端问道21期实操教学,利用云数据库 Tair(兼容 Redis®)缓存实现高并发场景下的极速响应。主要内容分为四部分:方案概览、部署准备、一键部署和完成及清理。方案概览中,展示了如何使用 Redis 提升业务性能,降低响应时间;部署准备介绍了账号注册与充值步骤;一键部署详细讲解了创建 ECS、RDS 和 Redis 实例的过程;最后,通过对比测试验证了 Redis 缓存的有效性,并指导用户清理资源以避免额外费用。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等