ABP - 缓存模块(1)

这篇具有很好参考价值的文章主要介绍了ABP - 缓存模块(1)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

ABP - 缓存模块(1)

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

ABP - 缓存模块(1)

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

abp add-package  Volo.Abp.Caching

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

install-package Volo.Abp.Caching.StackExchangeRedis

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

ABP - 缓存模块(1)

同样的,模块初始化的时候也已经配置好 Redis 缓存使用的内容:

ABP - 缓存模块(1)

通过源码我们也可以看到,Redis 相应的配置是从配置文件中的 "Redis" 节点读取的,我们可以在appsettings.json 中添加以下内容启用 Redis 缓存:

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

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

[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 泛型接口即可:

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"));
	}
}

ABP - 缓存模块(1)

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)文章来源地址https://www.toymoban.com/news/detail-468371.html

到了这里,关于ABP - 缓存模块(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Abp vNext 模块加载机制

    文章目录 PreConfigureServices 添加依赖注入或者其它配置之前 ConfigureServices 添加依赖注入或者其它配置 PostConfigureServices 添加依赖注入或者其它配置之后 OnPreApplicationInitialization 初始化所有模块之前 OnApplicationInitialization 初始化所有模块 OnPostApplicationInitialization 初始化所有模块之

    2024年02月08日
    浏览(27)
  • Volo.Abp升级小记(二)创建全新微服务模块

    @ 目录 创建模块 领域层 应用层 数据库和仓储 控制器 配置微服务 测试微服务 微服务注册 添加资源配置 配置网关 运行项目 假设有一个按照官方sample搭建的微服务项目,并安装好了abp-cli。 需要创建一个名为GDMK.CAH.Common的模块,并在模块中创建标签管理功能 因为大部分的代

    2024年02月08日
    浏览(21)
  • C# abp框架Http辅助类

    为什么要定义接口而不直接使用静态类,因为接口可以注入缓存对象,这样就能从缓存中读取指定的请求头 1、支持https 2、支持从缓存读取鉴权请求头 3、支持对象携带文件上传 1、无法添加请求头content-type var s = new HttpRequestMessage() s.Headers.Add(\\\"Content-Type\\\", \\\"\\\") 报错:Misused head

    2023年04月25日
    浏览(19)
  • Abp框架Web站点的安全性提升

    本文将从GB/T 22239《信息安全技术 网络安全等级保护基本要求》规定的安全计算环境中解读、摘要若干安全要求,结合Abp框架,对站点进行安全升级。 设置密码最小长度 密码由数字、字母和特殊字符组合而成 设置密码每隔90天需要更换一次 在AbpSettings表中配置开启PasswordCom

    2023年04月18日
    浏览(22)
  • ABP中的[AllowAnonymous]作用是什么?

    [AllowAnonymous] 是一个特性标记(attribute),可以应用在 ASP.NET Core MVC 或者 Web API 应用程序的控制器或方法上。它的作用是允许未经认证的用户匿名访问带有身份验证限制的控制器和方法。 通常,授权过滤器需要用户进行身份验证才能访问带有 [Authorize] 特性标记的控制器或方法

    2024年02月05日
    浏览(21)
  • 系统学习Python——警告信息的控制模块warnings:为新版本的依赖关系更新代码

    分类目录:《系统学习Python》总目录 相关文章: 警告信息的控制模块 warnings 基础知识 警告类别 警告过滤器 基础知识 警告过滤器的执行过程 默认警告过滤器 暂时禁止警告 测试警告 为新版本的依赖关系更新代码 常用函数 warnings.warn warnings.warn_explicit warnings.showwarning warning

    2024年01月19日
    浏览(31)
  • Cocos独立游戏开发框架中的日志模块:Bug无所遁形

    本系列是《8年主程手把手打造Cocos独立游戏开发框架》,欢迎大家关注分享收藏订阅。 在Cocos独立游戏开发框架中,一个强大的日志模块是不可或缺的组成部分。日志不仅仅是记录应用程序的运行状态,还可以用于故障排除、性能监测和安全审计。本文将探讨如何设计和实现

    2024年02月09日
    浏览(31)
  • 我的框架-Unity3d中的用户数据储存模块UserDB

    前言:         我们在开发一些小游戏的时候,不可能将所有的数据都上传到服务器里去储存,有很多数据是需要储存到用户本地的。比如一些简单的用户设置,一些只需要打开一次的用户提示记录等等。当所需储存的数据比较少的时候,我们可以直接用 PlayerPrefs.SetString 直

    2023年04月14日
    浏览(30)
  • Unity UGUI的Outline(描边)组件的介绍及ABP框架提供的增删改查的基类使用

    Outline(描边)组件是Unity UGUI中的一种特效组件,用于给UI元素添加描边效果。通过设置描边的颜色、宽度和模糊程度,可以使UI元素在视觉上更加突出。 Outline(描边)组件通过在UI元素周围绘制多个相同的UI元素,并设置不同的颜色和大小,从而实现描边的效果。描边的宽度和模糊

    2024年02月21日
    浏览(31)
  • CPU、内存、缓存的关系

    (1)CPU(Central Processing Unit) 中央处理器 (2)内存 内存用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁,内存的运行决定计算机整体运行快慢。 缓冲区(Buffer)是内存空间的一部分,在内存中预留了一定的存储空间,用

    2024年02月08日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包