缓存在一个大型一点的系统里面是必然会涉及到的,合理的使用缓存能够给我们的系统带来更高的响应速度。由于数据提供服务涉及到数据库的相关操作,如果客户端的并发数量超过一定的数量,那么数据库的请求处理则以爆发式增长,如果数据库服务器无法快速处理这些并发请求,那么将会增加客户端的请求时间,严重者可能导致数据库服务或者应用服务直接瘫痪。缓存方案就是为这个而诞生,随着缓存的引入,可以把数据库的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();
}); //特殊指定为5分钟
Configuration.Caching.Configure("DictDataAppService", cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes();
});

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 = -;
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();
});
}

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("随访状态");
}

这样就非常简化了我们对字典数据源的绑定操作了,非常方便易读,下面是其中一个功能界面的下拉列表展示。

使用缓存接口,对于大量字典数据显示的界面,界面显示速度有了不错的提升。

ABP开发框架前后端开发系列---(15)ABP框架的服务端和客户端缓存的使用的更多相关文章

  1. ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架

    前面介绍了很多ABP系列的文章,一步一步的把我们日常开发中涉及到的Web API服务构建.登录日志和操作审计日志.字典管理模块.省份城市的信息维护.权限管理模块中的组织机构.用户.角色.权限.菜单等内 ...

  2. ABP开发框架前后端开发系列---(3)框架的分层和文件组织

    在前面随笔<ABP开发框架前后端开发系列---(2)框架的初步介绍>中,我介绍了ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以便基于数据库应用的简化处理.本篇随笔进一步对 ...

  3. ABP开发框架前后端开发系列---(2)框架的初步介绍

    在前面随笔<ABP开发框架前后端开发系列---(1)框架的总体介绍>大概介绍了这个ABP框架的主要特点,以及介绍了我对这框架的Web API应用优先的一些看法,本篇继续探讨ABP框架的初步 ...

  4. ABP开发框架前后端开发系列---(10)Web API调用类的简化处理

    在较早期的随笔<ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用>已经介绍了Web API调用类的封装处理,虽然这些调用类我们可以使用代码生成工具快 ...

  5. ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用

    在前面几篇随笔介绍了我对ABP框架的改造,包括对ABP总体的介绍,以及对各个业务分层的简化,Web API 客户端封装层的设计,使得我们基于ABP框架的整体方案越来越清晰化, 也越来越接近实际的项目开 ...

  6. ABP开发框架前后端开发系列---(4)Web API调用类的封装和使用

    在前面随笔介绍ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以及简化了ABP框架的各个层的内容,使得我们项目结构更加清晰.上篇随笔已经介绍了字典模块中应用服务层接口的实现情况,并且通过 ...

  7. ABP开发框架前后端开发系列---(11)菜单的动态管理

    在前面随笔<ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理>中介绍了基于ABP框架服务构建的Winform客户端,客户端通过Web API调用的方式进行获取数据,从而实现 ...

  8. ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理

    在前面两篇随笔<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>和<ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程>开始 ...

  9. ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程

    在前面随笔介绍的<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>里面,介绍了如何改进和完善审计日志和登录日志的应用服务端和Winform客户端,由于篇幅限制,没有进 ...

  10. ABP开发框架前后端开发系列---(12)配置模块的管理

    一般来说,一个系统或多或少都会涉及到一些系统参数或者用户信息的配置,而ABP框架也提供了一套配置信息的管理模块,ABP框架的配置信息,必须提前定义好配置的各项内容,然后才能在系统中初始化或者通过接口查 ...

随机推荐

  1. SVN如何迁徙到Git

    由于一直都是采用SVN作为源码管理,转到Git不能从头开始吧~~下面就具体说说如何将SVN已有的项目工程迁徙到Git上面 步骤 (1) 安装Git客户端程序(2) 将SVN上面的工程通过Git克隆到本 ...

  2. XF 键盘类型设置

    键盘类型: default Chat-输入短信或表情 Email Numeric Telephone Url-输入网址和文件路径 其他额外选项: CapitalizeSentence SpellChe ...

  3. Marker

    # 样例 <?xml version="1.0" encoding="UTF-8"?> <Configuration status=" ...

  4. wcf中的消息模式

    1请求响应模式 a.wcf中的消息模式默认是请求响应模式 b.返回值是void默认也是请求响应模式,可返回服务端的错误信息 c.客户端在请求后,当前线程停止真到接受收服务器的响应 [Opereatio ...

  5. Android 查看APK文件的签名算法

    查看APK使用了什么签名算法 keytool -list -printcert -jarfile xxx.apk 示例: 签名者 #1: 签名: 所有者: 发布者: 序列号: 有效期开始日期: 证书指 ...

  6. 深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数

    原文:深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数 概述 COLUMNS_UPDATED函数能够出现在INSERT或UPDATE触发器中AS关键字后的任何位置,用来 ...

  7. 分配粒度和内存页面大小(x86处理器平台的分配粒度是64K,内存页是4K,所以section都是0x1000对齐,硬盘扇区大小是512字节,所以PE文件默认文件对齐是0x200)

    分配粒度和内存页面大小 x86处理器平台的分配粒度是64K,32位CPU的内存页面大小是4K,64位是8K,保留内存地址空间总是要和分配粒度对齐.一个分配粒度里包含16个内存页面. 这是个概念,具体不 ...

  8. WPF 调用API修改窗体风格实现真正的无边框窗体

    原文:WPF 调用API修改窗体风格实现真正的无边框窗体 WPF中设置无边框窗体似乎是要将WindowStyle设置为None,AllowTransparency=true,这样才能达到WinForm ...

  9. OWIN 托管服务器问题:StartOptions WebApp.Start TargetInvocationException

    我有一个与OWIN托管的服务器有一个小问题.我试图让它可以访问本地网络,这意味着我不得不添加一些额外的选择: // Start OWIN host StartOptions options = new ...

  10. Android零基础入门第63节:过时但仍值得学习的选项卡TabHost

    原文:Android零基础入门第63节:过时但仍值得学习的选项卡TabHost 由于前几天参加一个学习培训活动,几乎每天都要从早晨7点到晚上一两点,没有什么时间来分享,实在抱歉中间断更了几天.从今天开 ...