ABP - 缓存模块(1)

ABP - 缓存模块(1)

  • 1. 与 .NET Core 缓存的关系和差异
  • 2. Abp 缓存的使用
    • 2.1 常规使用
    • 2.2 非字符串类型的 Key
    • 2.3 批量操作
  • 3. 额外功能

1. 与 .NET Core 缓存的关系和差异

ABP 框架中的缓存系统核心包是 Volo.Abp.Caching ,而对于分布式缓存的支持,abp 官方提供了基于 Redis 的方案,需要安装 Volo.Abp.Caching.StackExchangeRedis 集成包。默认的情况下,在我们使用 ABP CLI 创建 ABP 框架模板项目的时候已经集成了这个包,我们不需要手动进行安装。

ABP 框架中的缓存系统在 ASP.NET Core的分布式缓存系统上进行扩展,从而使分布式缓存使用起来更加方便。理所当然的,ABP 框架的缓存系统兼容 ASP.NET Core 原生的分布式缓存使用方式,关于 ASP.NET Core 分布式缓存的使用,可以参考我之前的文章 ASP.NET Core - 缓存之分布式缓存,也可以看下官方文档 ASP.NET Core 中的分布式缓存 。

那么 ABP 框架中的缓存系统扩展了什么呢?ASP.NET Core 的分布式缓存又有哪些不便之处呢 ?ASP.NET Core 通过 IDistributedCache 抽象了分布式缓存的存取操作,但是有这样两个问题:

  • 它对缓存值的存取是基于 byte 数组的,而不是对象。

    这使得我们在使用分布式缓存的时候需要先将实例对象进行序列化/反序列化,之后再转码为 byte 数组。如果每个使用分布式缓存的地方都这么做将会有很多的重复冗余的代码,这是需要抽取封装的。

  • 为了能实现多个应用公用缓存,达成分布式缓存的作用,它将所有的缓存项存放在同一个 Key 池之中。

    这就可能会有问题,我们要特别注意缓存键的设置,如果开发人员不注意,很可能将一些缓存相互覆盖了,特别是共用缓存的应用比较多,或者多租户的情况下,这造成的问题可能很严重,而且很难排查。

ABP 框架的缓存系统扩展了通用的泛型接口 IDistributedCache<TCacheItem> ,泛型类型就是缓存值的类型,用于解决上面提到的问题:

  • 该接口内部实现了对缓存对象 序列化/反序列化 的逻辑。 默认使用 JSON 序列化, 我们如果有需要可以替换 依赖注入 系统中 IDistributedCacheSerializer 服务的实现来覆盖默认的方式。

  • 该接口会根据缓存中对象类型自动向缓存key添加 缓存名称 前缀。 默认缓存名是缓存对象的全类名(如果类名以CacheItem 结尾, 那么CacheItem 会被忽略,不应用到缓存名称上),开发人员也可以在缓存类上使用 CacheName 特性 设置缓存的名称.

  • 如果是多租户应用的话,它会自动将当前的租户id添加到缓存键中, 以区分不同租户的缓存项 。 如果租户之间需要共享缓存对象, 我们可以在缓存类打上 IgnoreMultiTenancy 特性,声明当前缓存不区分租户。

  • 允许为每个应用程序定义 全局缓存键前缀 , 不同的应用程序可以在共享的分布式缓存中拥有自己的隔离池.

  • 它提供了 错误容忍 机制,对分布式缓存存取过程中的异常进行了处理,例如分布式缓存服务连接失败等,避免因缓存问题导致应用出错。
    因为缓存本身就只是一种用于提升应用性能,提升用户体验的策略,如果因为分布式缓存的问题导致应用出错,业务逻辑无法继续执行,那就得不偿失了。缓存系统应该是,就算将其从应用中摘除,也不影响应用业务逻辑正常执行的东西,只是性能可能差了点。

  • 它额外提供了 GetManyAsync 和 SetManyAsync 等方法, 支持对缓存的批量操作,可以显著提高批处理的性能。

2. Abp 缓存的使用

这里还是以控制台应用作为演示,Web 应用中使用比控制台更简单,因为 Web 应用中已经集成了缓存模块,不需要再自行引入。首先,通过以下命令生成一个 ABP 的控制台启动模板:

abp new AbpCacheSample -t console

之后,如果是使用基于内存的分布式缓存的话,只需要安装 Volo.Abp.Caching 包即可。

Install-package  Volo.Abp.Caching

再之后,在项目的模块文件中添加模块依赖:

在这里插入图片描述
这里不需要再进行依赖关系的配置,因为在 AbpCacheModule 模块的初始化之中已经配置好了相关的内容,通过源码可以看到:

在这里插入图片描述
当然,你也可以更高效地在项目所在的文件夹使用以下命令,省掉对模块依赖的配置。

abp add-package  Volo.Abp.Caching

如果是使用 Redis 分布式缓存的话,需要安装 Volo.Abp.Caching.StackExchangeRedis 集成包,这个包对 Microsoft.Extensions.Caching.StackExchangeRedis 进行了扩展,它简化了 Redis 缓存的配置,也是前面提到的 GetManyAsync 和SetManyAsync 等更加性能的方法的实现所在。

install-package Volo.Abp.Caching.StackExchangeRedis

同时,模块依赖改成以下这样:

在这里插入图片描述
同样的,模块初始化的时候也已经配置好 Redis 缓存使用的内容:

在这里插入图片描述
通过源码我们也可以看到,Redis 相应的配置是从配置文件中的 “Redis” 节点读取的,我们可以在appsettings.json 中添加以下内容启用 Redis 缓存:

{"Redis": {"IsEnabed": true, // 控制 Redis 分布式缓存是否启用"Configuration": "xxx.xxx.xxx.xxx:6379,password=123456" // Redis 连接字符串}
}

这里其实就是通过配置信息中的 “Redis:Configuration” 节点配置 RedisCacheOpions 选项,这个选项是微软标准的 Redis 缓存支持包中的,如果有需要,我们可以在代码中通过以下方式对该选项进行配置:

[DependsOn(typeof(AbpAutofacModule),typeof(AbpCachingStackExchangeRedisModule)
)]
public class AbpCacheSampleModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){Configure<RedisCacheOptions>(option =>{// ...});}
}

2.1 常规使用

首先我们定义缓存类:

/// <summary>
/// 缓存中,会以缓存类的全类名作为建,如果类名以 CacheItem 结尾,CacheItem 会被忽略
/// </summary>
// [CacheName("DateTime")] 也可以通过 CacheName 特性设置缓存键
public class DateTimeCacheItem
{public DateTime Now { get; set; }public string Name { get; set; }
}

之后,只需要在要用到的服务中注入 IDistributedCache<CacheItem> 泛型接口即可:

public class HelloWorldService : ITransientDependency
{public const string CacheKey = nameof(HelloWorldService);private readonly IDistributedCache<DateTimeCacheItem> _distributedCache;public HelloWorldService(IDistributedCache<DateTimeCacheItem> distributedCache){_distributedCache = distributedCache;}public async Task SayHelloAsync(){// 常规的 IDisctributedCache 的同名方法,不过 ABP 框架中进行了扩展// 使其支持泛型,可以直接存取对象//var cacheValue = await _distributedCache.GetAsync(CacheKey);//if (cacheValue == null)//{//    cacheValue = new DateTimeCacheItem//    {//        Name = CacheKey,//        Now = DateTime.Now,//    };//    await _distributedCache.SetAsync(//        CacheKey, // 缓存键,最终的键会是 缓存名称(缓存类全类名去除CacheItem,或CacheName特性设置的名称) + 这里的键//        cacheValue, // 直接存取对象,而不用自己序列化/反序列化 以及转码 //        new DistributedCacheEntryOptions // 一样可以通过选项设置缓存策略//    {//        SlidingExpiration = TimeSpan.FromMinutes(1)//    });//}// ABP 框架新增的方法var cacheValue = await _distributedCache.GetOrAddAsync(CacheKey,async () =>{return await Task.FromResult(new DateTimeCacheItem(){Name = CacheKey,Now = DateTime.Now});},() => new DistributedCacheEntryOptions{SlidingExpiration= TimeSpan.FromMinutes(1)});Console.WriteLine(JsonSerializer.Serialize(cacheValue));Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));}
}

在这里插入图片描述

2.2 非字符串类型的 Key

除了可以使用 string 类型作为缓存键之外,我们还可以通过 IDistributedCache<CacheKey, CacheItem> 接口使用其他类型,甚至复杂类型作为缓存键,使用复杂类型作为缓存键的时候需要重写 ToString() 方法。

public class MyCacheKey{public int Id { get; set; }public string Name { get; set; }public override string ToString(){return Id + Name;}}public class HelloWorldService : ITransientDependency
{private readonly IDistributedCache<DateTimeCacheItem, int> _distributedCacheKeyInt;private readonly IDistributedCache<DateTimeCacheItem, MyCacheKey> _distiributedCacheKey;public HelloWorldService(IDistributedCache<DateTimeCacheItem, int> distributedCacheKeyInt,IDistributedCache<DateTimeCacheItem, MyCacheKey> distiributedCacheKey){_distributedCache = distributedCache;}public async Task SayHelloAsync(){_ = await _distributedCacheKeyInt.GetOrAddAsync(1,async () =>{return await Task.FromResult(new DateTimeCacheItem{Name = "1",Now = DateTime.Now});},() => new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) });_ = await _distiributedCacheKey.GetOrAddAsync(new MyCacheKey { Id = 1, Name = "MyKey" },async () =>{return await Task.FromResult(new DateTimeCacheItem{Name = "1",Now = DateTime.Now});},() => new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) });}
}

2.3 批量操作

批量进行缓存存取操作的方式如下:

_ = await _distributedCache.GetOrAddManyAsync(new List<string> { "Key1", "Key2" }, async keys =>
{return await Task.FromResult(keys.Select(k => new KeyValuePair<string, DateTimeCacheItem>(k, new DateTimeCacheItem { Name = k, Now = DateTime.Now })).ToList());
},
() => new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) });

ABP的分布式缓存接口定义了以下批量操作方法,当你需要在一个方法中调用多次缓存操作时,这些方法可以提高性能

  • SetManyAsync 和 SetMany 方法可以用来向缓存中设置多个值.
  • GetManyAsync 和 GetMany 方法可以用来从缓存中获取多个值.
  • GetOrAddManyAsync 和 GetOrAddMany 方法可以用来从缓存中获取并添加缺少的值.
  • RefreshManyAsync 和 RefreshMany 方法可以来用重置多个值的滚动过期时间.
  • RemoveManyAsync 和 RemoveMany 方法可以用来从缓存中删除多个值.

这些不是标准的ASP.NET Core缓存方法, 所以其他的分布式缓存方案可能不支持。而 ABP 通过 Volo.Abp.Caching.StackExchangeRedis 实现了 Redis 分布式缓存下的批量操作。如果采用了其他方案实现分布式缓存,而提供程序不支持的情况下,会回退到循环调用 SetAsync 和 GetAsync 方法。

以上就是 ABP 框架中缓存系统的基本使用。除此之外,ABP 框架提供了 AbpDistributedCacheOptions 选项用于配置一些缓存策略,可用属性:

  • HideErrors (bool, 默认: true): 启用/禁用隐藏从缓存服务器写入/读取值时的错误.
  • KeyPrefix (string, 默认: null): 如果你的缓存服务器由多个应用程序共同使用, 则可以为应用程序的缓存键设置一个前缀. 在这种情况下, 不同的应用程序不能覆盖彼此的缓存内容.
  • GlobalCacheEntryOptions (DistributedCacheEntryOptions): 用于设置保存缓内容却没有指定选项时, 默认的分布式缓存选项 (例如 AbsoluteExpiration 和 SlidingExpiration). SlidingExpiration的默认值设置为20分钟.

3. 额外功能

另外,还有 ABP 框架下的缓存系统还有以下一些功能:

  • 错误处理

    当为你的对象设计缓存时, 通常会首先尝试从缓存中获取值. 如果在缓存中找不到该值, 则从来源查询对象. 它可能在数据库中, 或者可能需要通过HTTP调用远程服务器.

    在大多数情况下, 你希望容忍缓存错误; 如果缓存服务器出现错误, 也不希望取消该操作. 相反, 你可以默默地隐藏(并记录)错误并从来源查询. 这就是ABP框架默认的功能.

    ABP的分布式缓存异常处理, 默认记录并隐藏错误,有一个全局修改该功能的选项.所有的·IDistributedCache· (和 ·IDistributedCache<TCacheItem, TCacheKey>·)方法都有一个可选的参数 hideErrors, 默认值为null. 如果此参数设置为null, 则全局生效, 否则你可以选择单个方法调用时隐藏或者抛出异常.

  • 工作单元级别的缓存

    分布式缓存服务提供了一个有趣的功能. 假设你已经更新了数据库中某本书的价格, 然后将新价格设置到缓存中, 以便以后使用缓存的值. 如果设置缓存后出现异常, 并且更新图书价格的事务被回滚了, 该怎么办?在这种情况下, 缓存值是错误的.

    IDistributedCache<…>方法提供一个可选参数, considerUow, 默认为false. 如果将其设置为true, 则你对缓存所做的更改不会应用于真正的缓存存储, 而是与当前的工作单元关联. 你将获得在同一工作单元中设置的缓存值, 但仅当前工作单元成功时更改才会生效.

  • 可替换的键、值处理方式

    • IDistributedCacheSerializer

      IDistributedCacheSerializer 服务用于序列化和反序列化缓存内容. 默认实现是 Utf8JsonDistributedCacheSerializer 类, 它使用 IJsonSerializer 服务将对象转换为 JSON, 反之亦然. 然后, 它使用 UTC8 编码将 JSON 字符串转换为分布式缓存接受的字节数组.

      如果你想实现自己的序列化逻辑, 可以自己实现并替换此服务.

    • IDistributedCacheKeyNormalizer

      默认情况下, IDistributedCacheKeyNormalizer 是由 DistributedCacheKeyNormalizer 类实现的. 它将缓存名称、应用程序缓存前缀和当前租户id添加到缓存键中. 如果需要更高级的键规范化, 可以自己实现并替换此服务。



参考文章:
ABP 官方文档 - 缓存



ABP 系列总结:
目录:ABP 系列总结
上一篇:ABP - 依赖注入(2)
下一篇:ABP - 缓存模块(2)

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

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

相关文章

【RAG落地利器】向量数据库Chroma入门教程

安装部署 官方有pip安装的方式&#xff0c;为了落地使用&#xff0c;我们还是采用Docker部署的方式&#xff0c;参考链接来自官方部署: https://cookbook.chromadb.dev/running/running-chroma/#docker-compose-cloned-repo 我们在命令终端运行&#xff1a; docker run -d --…

基于Python django的音乐用户偏好分析及可视化系统设计与实现

1.1 论文背景 随着信息技术的快速发展&#xff0c;在线音乐服务已成为日常生活的重要组成部分。QQ音乐&#xff0c;凭借其创新的音乐推荐算法和独特的社交特性&#xff0c;成功在竞争激烈的市场中获得一席之地。该平台的歌单文化和评论文化不仅满足了用户自尊和自我实现的需求…

以Python构建ONE FACE管理界面:从基础至进阶的实战探索

一、引言 1.1 研究背景与意义 在人工智能技术蓬勃发展的当下,面部识别技术凭借其独特优势,于安防、金融、智能终端等众多领域广泛应用。在安防领域,可助力监控系统精准识别潜在威胁人员,提升公共安全保障水平;金融行业中,实现刷脸支付、远程开户等便捷服务,优化用户体…

以单用户模式启动 Linux 的方法

注&#xff1a;本文为 “Linux 启动单用户模式” 相关文章合辑。 未整理去重。 以单用户模式启动 linux 的三种方法 作者&#xff1a; Magesh Maruthamuthu 译者&#xff1a; LCTT Xiaobin.Liu 2020-05-03 23:01 单用户模式&#xff0c;也被称为维护模式&#xff0c;超级用户…

【C++】size_t全面解析与深入拓展

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;一、什么是size_t&#xff1f;为什么需要size_t&#xff1f; &#x1f4af;二、size_t的特性与用途1. size_t是无符号类型示例&#xff1a; 2. size_t的跨平台适应性示例对…

YOLOv9改进,YOLOv9检测头融合RFAConv卷积,适合目标检测、分割任务

摘要 空间注意力已广泛应用于提升卷积神经网络(CNN)的性能,但它存在一定的局限性。作者提出了一个新的视角,认为空间注意力机制本质上解决了卷积核参数共享的问题。然而,空间注意力生成的注意力图信息对于大尺寸卷积核来说是不足够的。因此,提出了一种新型的注意力机制—…

Mysql触发器(学习自用)

一、介绍 二、触发器语法 注意&#xff1a;拿取新的数据时用new&#xff0c;旧数据用old。

即现软著工具 - 让软著申请更高效

在软件著作权申请的过程中&#xff0c;开发者常常会遇到代码整理、统计和生成证明文件等繁琐且复杂的任务。为了解决这些问题&#xff0c;提高申请效率和成功率&#xff0c;给大家介绍一款工具&#xff1a;即现软著工具。 即现软著工具&#xff0c;能够快速整理软著申请的程序鉴…

Java Web开发高级——单元测试与集成测试

测试是软件开发的重要环节&#xff0c;确保代码质量和功能的正确性。在Spring Boot项目中&#xff0c;单元测试和集成测试是常用的两种测试类型&#xff1a; 单元测试&#xff1a;测试单个模块&#xff08;如类或方法&#xff09;是否按预期工作。集成测试&#xff1a;测试多个…

路径规划之启发式算法之二十八:候鸟优化算法(Migrating Birds Optimization, MBO)

候鸟优化算法(Migrating Birds Optimization, MBO)是一种基于群体智能的元启发式优化算法,其灵感来源于候鸟迁徙时的“V”字形飞行队列。这种队列结构能够有效减少能量消耗,同时提高飞行效率。MBO算法通过模拟候鸟的迁徙行为,利用群体间的协作和信息共享来优化问题的解。 …

Observability:最大化可观察性 AI 助手体验的 5 大提示(prompts)

作者&#xff1a;来自 Elastic Zoia_AUBRY 在过去三年担任客户工程师期间&#xff0c;我遇到了数百名客户&#xff0c;他们最常问的问题之一是&#xff1a;“我的数据在 Elastic 中&#xff1b;我该如何利用它获得最大优势&#xff1f;”。 如果这适用于你&#xff0c;那么本…

C# 委托和事件思维导图

思维导图 下载链接腾讯云盘 https://share.weiyun.com/fxBH9ESl

2024.ailx10的年终总结

已经工作7年啦&#xff0c;今年网络安全行业愈发寒冷&#xff0c;几乎所有友商都在做安全GPT&#xff0c;说实话&#xff0c;AI确实颠覆了传统的网络安全运营&#xff0c;以前需要安服处置告警&#xff0c;以后可能就不需要了&#xff0c;大家日子都不好过&#xff0c;越是简单…

机器学习(5):支持向量机

1 介绍 支持向量机&#xff08;Support Vector Machine&#xff0c;简称 SVM&#xff09;是一种监督学习算法&#xff0c;主要用于分类和回归问题。SVM 的核心思想是找到一个最优的超平面&#xff0c;将不同类别的数据分开。这个超平面不仅要能够正确分类数据&#xff0c;还要使…

AI需要的基础数学知识

AI&#xff08;人工智能&#xff09;涉及多个数学领域&#xff0c;以下是主要的基础数学知识&#xff1a; 1. 线性代数 矩阵与向量&#xff1a;用于表示数据和模型参数。矩阵乘法&#xff1a;用于神经网络的前向传播。特征值与特征向量&#xff1a;用于降维和主成分分析&…

flutter跨端UI框架简介

flutter跨端UI框架简介 简介 Flutter是由Google开发的开源应用开发框架&#xff0c;主要用于构建高性能、跨平台的移动、Web和桌面应用程序。Flutter使用Dart语言&#xff0c;提供了一套丰富的Widgets&#xff0c;使开发者能够快速创建美观的用户界面。其最大特点是热重载功能…

找不到mfc140u,具体原因分析

mfc140u.dll 是 Microsoft Foundation Classes (MFC) 库的一部分&#xff0c;通常与使用 MFC 构建的应用程序一起分发。当应用程序尝试运行但找不到 mfc140u.dll 时&#xff0c;可能的原因包括但不限于以下几点&#xff1a; 1.文件缺失&#xff1a; •可能是在安装或更新过程中…

StarRocks 3.4 发布--AI 场景新支点,Lakehouse 能力再升级

自 StarRocks 3.0 起&#xff0c;社区明确了以 Lakehouse 为核心的发展方向。Lakehouse 的价值在于融合数据湖与数据仓库的优势&#xff0c;能有效应对大数据量增长带来的存储成本压力&#xff0c;做到 single source of truth 的同时继续拥有极速的查询性能&#xff0c;同时也…

[答疑]这个消息名是写发送数据还是接收数据

睡鱼(61***11) 16:08:29 睡鱼(61***11) 16:08:58 他们说这个图有问题 UML菜鸟(1***22) 16:10:55 有点暗 睡鱼(61***11) 16:27:50 顺序图里面的箭头代表消息还是职责 睡鱼(61***11) 16:28:08 比如 a往b发送数据 睡鱼(61***11) 16:28:36 这个消息名是写发送数据还是接收数据 睡…

WPS按双字段拆分工作表到独立工作簿-Excel易用宝

我们老板真是事多&#xff0c;他说要把这个工作表以月份和支付方式的维度&#xff0c;以这两个字段进行拆分工作表&#xff0c;而且拆分出来的表格要保存一个新的工作簿。 啥事都交给我&#xff0c;他还以为我有三头六臂呢&#xff0c;还好我有易用宝&#xff0c;可以轻松拆分…