缓存在一个大型一点的系统里面是必然会涉及到的, 合理的使用缓存能够给我们的系统带来更高的响应速度. 由于数据提供服务涉及到数据库的相关操作, 如果客户端的并发数量超过一定的数量, 那么数据库的请求处理则以爆发式增长, 如果数据库服务器无法快速处理这些并发请求, 那么将会增加客户端的请求时间, 严重者可能导致数据库服务或者应用服务直接瘫痪. 缓存方案就是为这个而诞生, 随着缓存的引入, 可以把数据库的 IO 耗时操作, 转换为内存数据的快速响应操作, 或者把整个页面缓存到缓存系统里面. 本篇随笔主要介绍利用 ABP 框架的支持实现的服务端缓存处理和 Winform 客户端缓存的处理.
1, 缓存文章回顾
缓存的重要性不言而喻, 我在博客园里面也写了很多缓存相关的文章, 都是基于实际系统的总结处理.
《Winform 里面的缓存使用》
《使用 ConcurrentDictionary 替代 Hashtable 对多线程的对象缓存处理》
《在. NET 项目中使用 PostSharp, 使用 MemoryCache 实现缓存的处理》
《.NET 缓存框架 CacheManager 在混合式开发框架中的应用(1)-CacheManager 的介绍和使用》
《在. NET 项目中使用 PostSharp, 使用 CacheManager 实现多种缓存框架的处理》
《在 Winform 开发框架中下拉列表绑定字典以及使用缓存提高界面显示速度》
《C# 开发微信门户及应用(48) - 在微信框架中整合 CacheManager 缓存框架》
上面这些都是和缓存相关的内容, 一般来说, 缓存有很多方式的实现, 如 MemoryCache,Redis,Memcached,Couchbase,System.web.Caching 等, 为了方便我们一般使用. net 的内存缓存处理, 如果我们需要序列化缓存内容, 那么可以采用 MemoryCache 或者 Redis 缓存等. 后来我们通过综合考虑, 基于配置方式选择不同缓存方式, 在后端一般可以使用 CacheManager 的缓存处理.
如下面是基于常规架构的缓存处理分层, 如果是基于 Web API 的服务端, 那么缓存一般可以在 Web API 层或者它的下面一层.
如果是基于可序列化的缓存处理, 它在 IIS 或者其他 Web 容器重新启动后, 缓存不会丢失, 如在 Redis 里面, 有相关的缓存记录如下所示.
2,ABP 服务端缓存处理
ABP 提供了缓存的抽象, 它内部使用了这个缓存抽象. 虽然默认的实现使用了 MemoryCache, 通过配置也可以使用 Redis 等缓存, 缓存的主要接口 ICacheManager.
我们可以在应用服务层的构造函数里面, 注入该接口, 然后使用该接口获得一个缓存对象.
官方简单的应用服务层代码如下所示.
- public class TestAppService : ApplicationService
- {
- private readonly ICacheManager _cacheManager;
- public TestAppService(ICacheManager cacheManager)
- {
- _cacheManager = cacheManager;
- }
实际上, 我们应用服务层应该会更加复杂一些, 如下是我们 ABP 快速开发框架的应用服务层的代码
- [AbpAuthorize]
- public class DictDataAppService : MyAsyncServiceBase<DictData, DictDataDto, string, DictDataPagedDto, CreateDictDataDto, DictDataDto>, IDictDataAppService
- {
- /// <summary>
- /// 缓存管理接口
- /// </summary>
- private readonly ICacheManager _cacheManager;
- private readonly IRepository<DictData, string> _repository;
- public DictDataAppService(IRepository<DictData, string> repository, ICacheManager cacheManager) : base(repository)
- {
- _repository = repository;
- _cacheManager = cacheManager;// 依赖注入缓存
- }
对于字典模块, 我们一般获取接口如下所示.
- /// <summary>
- /// 根据字典类型 ID 获取所有该类型的字典列表集合(Key 为名称, Value 为值)
- /// </summary>
- /// <param name="dictTypeId">字典类型 ID</param>
- /// <returns></returns>
- public async Task<Dictionary<string, string>> GetDictByTypeID(string dictTypeId)
- {
- IList<DictData> list = await Repository.GetAllListAsync(s => s.DictType_ID == dictTypeId);
- Dictionary<string, string> dict = new Dictionary<string, string>();
- foreach (DictData info in list)
- {
- if (!dict.ContainsKey(info.Name))
- {
- dict.Add(info.Name, info.Value);
- }
- }
- return dict;
- }
如果我们需要把它构建一个缓存接口, 那么处理方式就是对它进行一个简单包装即可, 如下代码所示.
- /// <summary>
- /// 根据字典类型 ID 获取所有该类型的字典列表集合(使用缓存)
- /// </summary>
- /// <param name="dictTypeId">字典类型 ID</param>
- /// <returns></returns>
- public async Task<Dictionary<string, string>> GetDictByTypeIDCached(string dictTypeId)
- {
- // 系统缓存默认为 60 分钟, 可以在模块中配置具体的时间, 配置后则是具体配置时间
- return await _cacheManager.GetCache("DictDataAppService")
- .GetAsync(dictTypeId, () => GetDictByTypeID(dictTypeId));
- }
默认缓存超时是 60 分钟, 它可以改. 如果你超过 60 分钟没有使用缓存中的项, 会从缓存中自动移除. 你可以配置指定的缓存或是全部的缓存.
我们可以在应用服务层模块类 ApplicationModule 类里面进行修改, 实现对缓存的过期设置.
- // 系统缓存默认为 60 分钟, 可以在模块中配置具体的时间, 配置后则是具体配置时间
- // 所有缓存设置为 2 小时
- Configuration.Caching.ConfigureAll(cache =>
- {
- cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2);
- });
- // 特殊指定为 5 分钟
- Configuration.Caching.Configure("DictDataAppService", cache =>
- {
- cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes(5);
- });
Redis 缓存集成
默认缓存管理使用的是内存缓存. 所以, 如果你有多个并发的 Web 服务器使用同个应用, 可能会成为一个问题, 在这种情况下, 你需要一个分布 / 集中缓存服务, 你就可以简单的使用 Redis 做为你的缓存服务器.
Redis 是一个开源的使用 ANSI C 语言编写, 支持网络, 可基于内存亦可持久化的日志型, Key-Value 数据库, 和 Memcached 类似, 它支持存储的 value 类型相对更多, 包括 string(字符串),list(链表),set(集合),zset(sorted set -- 有序集合)和 hash(哈希类型). 在此基础上, Redis 支持各种不同方式的排序. 与 Memcached 一样, 为了保证效率, 数据都是缓存在内存中. 区别的是 Redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件, 并且在此基础上实现了 master-slave(主从)同步.
Redis 的代码遵循 ANSI-C 编写, 可以在所有 POSIX 系统 (如 Linux, *BSD, Mac OS X, Solaris 等) 上安装运行. 而且 Redis 并不依赖任何非标准库, 也没有编译参数必需添加.
下载地址: https://github.com/MSOpenTech/redis/releases 下载, 安装为 Windows 服务即可.
安装后作为 Windows 服务运行, 安装后可以在系统的服务里面看到 Redis 的服务在运行了, 如下图所示.
安装好 Redis 后, 还有一个 Redis 伴侣 Redis Desktop Manager 需要安装, 这样可以实时查看 Redis 缓存里面有哪些数据, 具体地址如下: http://redisdesktop.com/download
下载属于自己平台的版本即可
下载安装后, 打开运行界面, 如果我们往里面添加键值的数据, 那么可以看到里面的数据了.
我们来看看如何在 ABP 框架中使用 Redis 缓存
我们现在应用服务层模块里面配置好使用 Redis, 如下代码所示
- [DependsOn(
- ................
- typeof(AbpRedisCacheModule) //Redis 缓存加入
- )]
- public class ApplicationModule : AbpModule
- {
- public override void PreInitialize()
- {
- ............
- // 使用 Redis 缓存
- int DatabaseId = -1;
- int.TryParse(AppSettingConfig.GetAppSetting("RedisCache", "DatabaseId"), out DatabaseId);
- string connectionString = AppSettingConfig.GetAppSetting("RedisCache", "ConnectionString");
- Configuration.Caching.UseRedis(options =>
- {
- options.ConnectionString = connectionString;
- options.DatabaseId = DatabaseId;
- });
- // 系统缓存默认为 60 分钟, 可以在模块中配置具体的时间, 配置后则是具体配置时间
- // 所有缓存设置为 2 小时
- //Configuration.Caching.ConfigureAll(cache =>
- //{
- // cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2);
- //});
- // 特殊指定为 5 分钟
- Configuration.Caching.Configure("DictDataAppService", cache =>
- {
- cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes(5);
- });
- }
Host 项目配置文件, Appsetting.JSON 配置文件如下所示, 增加 RedisCache 的配置节点.
使用缓存处理的应用服务层接口实现如下所示
- /// <summary>
- /// 根据字典类型 ID 获取所有该类型的字典列表集合(使用缓存)
- /// </summary>
- /// <param name="dictTypeId">字典类型 ID</param>
- /// <returns></returns>
- public async Task<Dictionary<string, string>> GetDictByTypeIDCached(string dictTypeId)
- {
- // 系统缓存默认为 60 分钟, 可以在模块中配置具体的时间, 配置后则是具体配置时间
- return await _cacheManager.GetCache("DictDataAppService").GetAsync(dictTypeId, () => GetDictByTypeID(dictTypeId));
- }
在测试接口页面中进行测试
查看缓存管理里面的内容, 可以发现已经具有值了, 如下所示.
这样我们就可以很容易的从内存缓存切换到 Redis 的缓存了.
实体缓存
虽然 ABP 缓存系统出于普通的目的, 但有一个 EntityCache 基类, 可帮你缓存实体. 如果我们通过它们的 Id 获取的实体, 我们可以用这个基类缓存它们, 就不用再频繁地从数据库查询.
不过这里不对这个进行细讲了.
3,Winform 客户端的缓存处理
除了在服务端进行缓存测试外, 为了提高客户端的响应速度, 我们还可以在 Winform 客户端中使用内存缓存进行缓存一些不常变化的内容的, 这样可以避免频繁的请求网络接口, 获取接口数据.
ABP 基础模块里面也提供了一个简单的缓存类, 我们可以使用它进行缓存处理.
我曾经在之前一篇随笔《在 Winform 开发框架中下拉列表绑定字典以及使用缓存提高界面显示速度》对字典模块中使用缓存进行了说明, 这个我们也可以调整为 ABP 快速开发框架中 Winform 客户端的字典处理方式.
ABP 中有两种 cache 的实现方式: MemroyCache 和 RedisCache. 如下图, 两者都继承至 ICache 接口. ABP 核心模块封装了 MemroyCache 来实现 ABP 中的默认缓存功能. Abp.RedisCache 这个模块封装 RedisCache 来实现缓存.
我们可以在 Winform 客户端中使用 AbpMemoryCache 是实现内存缓存的处理.
例如我们在界面模块中使用一个字典辅助类来封装对字典模块的调用, 同时可以使用缓存方式进行获取.
使用缓存处理的逻辑, 如下所示
主要就是判断键值是否存在, 否则就设置内存缓存即可.
然后在编写一个字典控件的扩展函数, 如下所示.
- /// <summary>
- /// 绑定下拉列表控件为指定的数据字典列表
- /// </summary>
- /// <param name="control">下拉列表控件</param>
- /// <param name="dictTypeName">数据字典类型名称</param>
- /// <param name="defaultValue">控件默认值</param>
- /// <param name="emptyFlag">是否添加空行</param>
- public static void BindDictItems(this ComboBoxEdit control, string dictTypeName, string defaultValue, bool isCache = true, bool emptyFlag = true)
- {
- var dict = GetDictByDictType(dictTypeName, isCache);
- List<CListItem> itemList = new List<CListItem>();
- foreach (string key in dict.Keys)
- {
- itemList.Add(new CListItem(key, dict[key]));
- }
- control.BindDictItems(itemList, defaultValue, emptyFlag);
- }
绑定字典控件使用的时候, 就非常简单了, 如下代码是实际项目中对字典列表绑定的操作, 字典数据在字典模块里面统一定义的.
- /// <summary>
- /// 初始化数据字典
- /// </summary>
- private void InitDictItem()
- {
- txtInDiagnosis.BindDictItems("入院诊断");
- txtLeaveDiagnosis.BindDictItems("最后诊断");
- // 初始化代码
- this.txtFollowType.BindDictItems("随访方式");
- this.txtFollowStatus.BindDictItems("随访状态");
- }
这样就非常简化了我们对字典数据源的绑定操作了, 非常方便易读, 下面是其中一个功能界面的下拉列表展示.
使用缓存接口, 对于大量字典数据显示的界面, 界面显示速度有了不错的提升.
来源: https://www.cnblogs.com/wuhuacong/p/11174131.html