sns社区架构设计案例分享(二)
源码下载地址:http://www.jinhusns.com/Products/Download/?type=xcj
五、 架构使用说明 > 缓存 > 使用说明 >
(一)基础类库介绍

- ICacheService:缓存服务接口,供缓存的存取的调用;
 
ICacheService方法说明:
| 
 方法名称  | 
 成员修饰  | 
 说明  | 
 备注  | 
| 
 Add(多个参数):void +1重载  | 
 public static  | 
 加入缓存项  | 
|
| 
 Set(多个参数):void +1重载  | 
 public static  | 
 添加或更新缓存  | 
 如果无对应缓存项则创建,否则更新  | 
| 
 Remove(stringcacheKey):void  | 
 public static  | 
 移除缓存  | 
|
| 
 MarkDeletion(多个参数):void  | 
 public static  | 
 标识为删除  | 
|
| 
 Clear():void  | 
 public static  | 
 清空缓存  | 
|
| 
 Get(string cacheKey):object  | 
 public static  | 
 从缓存获取  | 
|
| 
 Get<T>(string cacheKey): T  | 
 public static  | 
 从缓存获取  | 
|
| 
 GetFromFirstLevel(stringcacheKey): object  | 
 public static  | 
 从一层缓存获取  | 
 分布式缓存情况下从分布式缓存获取  | 
| 
 GetFromFirstLevel<T>(string cacheKey) : T  | 
 public static  | 
 从一层缓存获取  | 
 分布式缓存情况下从分布式缓存获取  | 
1) 提供Tunynet.Caching.DefaultCacheService作为ICacheService的默认实现;
- CacheSettingAttribute:用于在实体标注缓存相关的设置;
 
CacheSettingAttribute属性说明:
| 
 属性名称  | 
 成员修饰  | 
 说明  | 
 备注  | 
| 
 EnableCache: bool  | 
 public  | 
 是否启用缓存  | 
 仅允许在构造器设置属性  | 
| 
 ExpirationPolicy : EntityCacheExpirationPolicies  | 
 public  | 
 实体缓存(实体正文缓存)过期策略  | 
|
| 
 PropertyNameOfBody : string  | 
 public  | 
 实体正文缓存对应的属性名称(如果不需单独存储实体正文缓存,则不要设置该属性)  | 
|
| 
 PropertyNamesOfArea : string  | 
 public  | 
 缓存分区的属性名称(可以设置多个,用逗号分隔)  | 
- RealTimeCacheHelper:主要功能是递增缓存版本号(一般无需开发人员干涉)和辅助获取缓存CacheKey;
 
RealTimeCacheHelper方法说明:
| 
 方法名称  | 
 成员修饰  | 
 说明  | 
 备注  | 
| 
 GetGlobalVersion():long  | 
 public  | 
 列表缓存全局version  | 
|
| 
 GetEntityVersion(objectprimaryKey): long  | 
 public  | 
 获取Entity的缓存版本  | 
|
| 
 GetAreaVersion(多个参数): long  | 
 public  | 
 获取列表缓存区域version  | 
|
| 
 GetCacheKeyOfEntity(object primaryKey): string  | 
 public  | 
 获取实体的cacheKey  | 
|
| 
 GetListCacheKeyPrefix(多个参数): string  | 
 public  | 
 获取列表缓存CacheKey的前缀(例如:abe3ds2sa90:8:)  | 
|
| 
 IncreaseEntityCacheVersion(object entityID): void  | 
 public  | 
 递增实体缓存(仅更新实体时需要递增)  | 
|
| 
 IncreaseListCacheVersion(IEntity entity): void  | 
 public  | 
 递增列表缓存version(仅增加、删除实体时需要递增)  | 
|
| 
 IncreaseAreaVersion(多个参数): void +2重载  | 
 public  | 
 递增列表缓存区域version  | 
(二)缓存时间过期类型
1. 预设以下几种过期时间(在Tunynet.Caching.CachingExpirationTypes定义);
| 
 缓存期限类型  | 
 描述  | 
 备注  | 
| 
 Invariable  | 
 永久不变的  | 
 
  | 
| 
 Stable  | 
 稳定数据  | 
 例如: Resources.xml、Area、Application  | 
| 
 RelativelyStable  | 
 相对稳定  | 
 例如: 权限配置、审核配置、敏感词、表情、站点类别、资讯版块、资讯版块集合  | 
| 
 UsualSingleObject  | 
 常用的单个对象  | 
 例如: 用户、群组、类别、标签、博客Section、相册Section、论坛版块、活动  | 
| 
 UsualObjectCollection  | 
 常用的对象集合  | 
 例如: 用户的朋友  | 
| 
 SingleObject  | 
 单个对象  | 
 例如: 博文、帖子  | 
| 
 ObjectCollection  | 
 对象集合  | 
 例如: 用于分页的私信数据  | 
- 目前实际使用的都是绝对过期时间(例如:5分钟过期);
 - 尽量不要在使用缓存时直接设置具体过期时间;
 - 可以配置过期时间因子(cacheExpirationFactor),用于统一调配预设过期时间类型对应的具体时间;
 
(三)缓存配置说明
缓存服务通过DI容器进行注册,例如:
//注册缓存
containerBuilder.Register(c => new DefaultCacheService(new RuntimeMemoryCache(), 1.0F)).As<ICacheService>().SingleInstance();
- 可以使用不同的构造函数实例化DefaultCacheService以支持分布式缓存,或者设置缓存过期时间因子(会整体影响预置的缓存过期时间);
 - 为性能考虑,必须注册成单例;
 
3. 如果自行实现ICacheService,同样在此注册替换掉DefaultCacheService;
(四)缓存开发注意事项
- 在分布式缓存情况下,如需更新缓存必须显式调用更新操作(ICacheService.Set()),因为web服务器与缓存服务器可能位于不同的服务器,而非分布式缓存情况下可以利用引用类型的特征直接更新从缓存获取的数据,而无需显示调用缓存更新操作(ICacheService.Set());
 - 所有可能需要分布式缓存的数据必须支持序列化/反序列化:
 
1) 需缓存的实体必须标注[Serializable];
2) 特殊数据类型必须验证是否支持序列化/反序列化,例如从Dictionary<T key,T value>派生的类型无法直接序列化;
3. 不要在使用Repository.GetTopEntities(inttopNumber, CachingExpirationTypescachingExpirationTypes, Func<string>getCacheKey, Func<PetaPoco.Sql>generateSql)设置CacheKey时与topNumber相关,因为不管topNumber值是多少,实际都会最多获取SecondaryMaxRecords条数据并缓存起来。即不同的topNumber可以共用一份缓存,用于提升缓存使用率,进而提升性能。
(五)实体的缓存标注
使用CacheSettingAttribute在实体上进行标注,使大部分缓存工作得以在Repository自动处理。
- 可以通过ExpirationPolicy设置实体的缓存策略(目前主要与缓存失效时间有关);
 - 可以通过PropertyNameOfBody设置实体正文缓存对应的属性名称;
 - 可以通过PropertyNamesOfArea设置列表分区缓存相关的属性,可以设置多个分区(即多个属性,用英文逗号分隔);
 
[CacheSetting(true, ExpirationPolicy = EntityCacheExpirationPolicies.Normal, PropertyNamesOfArea = "UserId,CategoryId", PropertyNameOfBody = "Body")]
[Serializable]
publicclassDiscussQuestion : IEntity
{
……
}
(六)使用实体正文缓存
当实体正文可能很大时,为了提升运行效率并减少分布式缓存时的网络流量将实体正文缓存单独存储,并且不在实体缓存中存储该部分内容。使用实体正文缓存,需要遵循以下步骤:
- 使用CacheSettingAttribute的PropertyNameOfBody在实体中进行标注,可以参见上一节的代码示例;
 - 在自行派生的Repository编写实体正文的获取方法,例如:
 
/// <summary>
///获取DiscussQuestion内容
/// </summary>
public string GetBody(long questionId)
{
string cacheKey = RealTimeCacheHelper.GetCacheKeyOfEntityBody(questionId);
string body = cacheService.Get<string>(cacheKey);
if (body == null)
{
DiscussQuestion question = Database.SingleOrDefault<DiscussQuestion>(questionId);
body = question != null ? question.Body : string.Empty;
cacheService.Add(cacheKey, body, CachingExpirationType.SingleObject);
}
return body;
}
(七)列表缓存使用
列表缓存的主要工作是CacheKey的获取。
- 1. 使用无版本的列表缓存
 
StringBuilder cacheKey = new StringBuilder(CacheSetting.GetListCacheKeyPrefix(CacheVersionTypes.None));
cacheKey.AppendFormat("Ranking:sb-{0}", (int)sortBy);
return cacheKey.ToString();
- 2. 使用有版本的列表缓存
 
1) 可以通过RealTimeCacheHelper的以下方法获取列表缓存CacheKey前缀,直接使用第一个方法是最简便的方式;
public string GetListCacheKeyPrefix(CacheVersionType cacheVersionType, string areaCachePropertyName, object areaCachePropertyValue);
2) 在使用查询条件类并且需要即时性缓存时,可以使查询条件类实现IListCacheSetting,例如:
///<summary>
/// DiscussQuestion查询条件封装
///</summary>
public class DiscussQuestionQuery : IListCacheSetting
{
public DiscussQuestionQuery(CacheVersionTypes cacheVersionType)
{
this.cacheVersionType = cacheVersionType;
}
……
#region IListCacheSetting 成员
Private CacheVersionTypes cacheVersionType = CacheVersionTypes.None;
///<summary>
///列表缓存版本设置
///</summary>
CacheVersionTypesIListCacheSetting.CacheVersionType
{
get { return cacheVersionType; }
}
private string areaCachePropertyName = null;
///<summary>
///缓存分区字段名称
///</summary>
public string AreaCachePropertyName
{
get { return areaCachePropertyName; }
set { areaCachePropertyName = value; }
}
private object areaCachePropertyValue = null;
///<summary>
///缓存分区字段值
///</summary>
public object AreaCachePropertyValue
{
get { return areaCachePropertyValue; }
set { areaCachePropertyValue = value; }
}
#endregion
}
3) 可以自行定义的Repository使用以下代码获取CacheKey:
StringBuilder cacheKey = new StringBuilder(RealTimeCacheHelper.GetListCacheKeyPrefix(query));
if (query.UserId.HasValue)
cacheKey.AppendFormat("UserID-{0}:", query.UserId.Value);
cacheKey.AppendFormat("sb-{0}:", (int)query.SortBy);
return cacheKey.ToString();
4) 在Service中使用以下代码构造DiscussQuestionQuery:
/// <summary>
/// 获取我创建的问题
/// </summary>
public PagingDataSet<DiscussQuestion> GetMyQuestions(long userId, int pageIndex)
{
DiscussQuestionQuery query = new DiscussQuestionQuery(CacheVersionType.AreaVersion);
query.AreaCachePropertyName = "UserId";
query.AreaCachePropertyValue = userId;
query.UserId = userId;
return questionRepository.GetQuestions(query, QuestionPageSize, pageIndex);
}
(八)如何使用实体以外的属性作为分区
使用实体以外的属性作为分区时,无法依靠Repository自动维护对应分区缓存版本,需要通过代码自行控制。
- 自行编写代码递增分区缓存版本,例如:
 
/// <summary>
/// 把用户加入到一组角色中
/// </summary>
public void AddUserToRoles(int userID, List<int> roleIDs)
{
var sql_delete = PetaPoco.Sql.Builder.Append("DELETE FROM tn_UsersInRoles where UserID=@0", userID);
List<PetaPoco.Sql> sql_inserts = new List<PetaPoco.Sql>();
foreach (var roleID in roleIDs)
{
var sql_insert = PetaPoco.Sql.Builder.Append("INSERT INTO tn_UsersInRoles (UserID,RoleID) VALUES (@0,@1)", userID, roleID);
sql_inserts.Add(sql_insert);
}
using (var scope = Database.GetTransaction())
{
Database.Execute(sql_delete);
Database.Execute(sql_inserts);
scope.Complete();
}
//递增缓存分区版本号(UserID)
RealTimeCacheHelper.IncreaseAreaVersion("UserID", userID);
}
- 借助RealTimeCacheHelper.GetListCacheKeyPrefix()获得CacheKey,例如:
 
/// <summary>
///获取用户的角色
/// </summary>
public IEnumerable<Role> GetRolesOfUser(int userID)
{
string cacheKey = RealTimeCacheHelper.GetListCacheKeyPrefix(CacheVersionTypes.AreaVersion, "UserID", userID);
IEnumerable<Role> roles = CacheService.Get<IEnumerable<Role>>(cacheKey);
if (roles == null)
{
var sql = PetaPoco.Sql.Builder
.Select("RoleID")
.From("tn_UsersInRoles")
.Where("UserID = @0", userID);
IList<object> roleIDs = Database.FetchFirstColumn(sql);
RoleRepository roleRepository = new RoleRepository();
roles = roleRepository.PopulateEntitiesByPrimaryKeys(roleIDs);
CacheService.Add(cacheKey, roles, CachingExpirationTypes.UsualObjectCollection);
}
return roles;
}
sns社区架构设计案例分享(二)的更多相关文章
- sns社区架构设计案例分享
		
今天看了个社区,了解了一下该产品的架构设计,做一下分享. 内容来源:http://www.jinhusns.com/Document/FrameworkDocument/?type=cc 一. 架构使 ...
 - web架构设计经验分享(转)
		
本人作为一位web工程师,着眼最多之处莫过于 性能与架构,本次幸得参与sd2.0大会,得以与同行广泛交流,于此二方面,有些心得,不敢独享,与众博友分享,本文是这次参会与众同撩交流的心得,有兴趣者可以查 ...
 - 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)(转)
		
1.写在前面 1.1.引言 如果在没有太多经验可借鉴的情况下,要设计一套完整可用的移动端IM架构,难度是相当大的.原因在于,IM系统(尤其是移动端IM系统)是多种技术和领域知识的横向应用综合体:网络编 ...
 - SOA架构设计经验分享—架构、职责、数据一致性
		
阅读目录: 1.背景介绍 2.SOA的架构层次 2.1.应用服务(原子服务) 2.2.组合服务 2.3.业务服务(编排服务) 3.SOA化的重构 3.1.保留服务空间,为了将来服务的组合 4.运用DD ...
 - [转]SOA架构设计经验分享—架构、职责、数据一致性
		
阅读目录: 1.背景介绍 2.SOA的架构层次 2.1.应用服务(原子服务) 2.2.组合服务 2.3.业务服务(编排服务) 3.SOA化的重构 3.1.保留服务空间,为了将来服务的组合 4.运用DD ...
 - 【转】SOA架构设计经验分享—架构、职责、数据一致性
		
1.背景介绍 最近一段时间都在做系统分析和设计工作,面对的业务是典型的重量级企业应用方向.突然发现很多以往觉得很简单的问题变得没有想象的那么容易,最大的问题就 是职责如何分配.论系统架 ...
 - 新年春节EDM邮件内容设计案例分享
		
春节自古以来都是中国最重要的节日.随着中国的发展,中国的春节在世界上都已受到越来越多人的关注,有许多国家和地区都将春节定为法定假日.与此同时,许多品牌营销商也选择在这个时候为中国客户发送马年新年邮件, ...
 - SOA架构设计案例分析
		
转载自:https://www.jdon.com/soa.html 首先Martin Fowler提出SOA歧义Service Oriented Ambiguity,认为"什么是SOA&qu ...
 - EDM设计案例分享:6款引人入胜的夏日邮件营销模板分享
		
夏日酷暑,清凉如风.在这个假期,旅游行业.酒店.服饰等都推出不少的假期活动,吸引游者的到来.假日期间,让我们看看一些旅游业.品牌服装店和酒店是怎么做好电子邮件广告的.在此,Focussend精心为大家 ...
 
随机推荐
- Delphi Android程序启动过程
			
Delphi的Android程序是原生的程序,也就是NativeActivity.那么就需要先看一下NativeActivity的原理, 在AndroidManifest.xml文件里面指定入口act ...
 - Linux文件和windows文件在 换行符的区别
			
Linux或Unix文件,和windows文件,在来回处理时,如果不注意 换行符的区别,可能导致程序错误!!!深刻的教训.... 在早期的打印机时代,开始新的一行要占用两个字符的时间.如果到了一行的结 ...
 - Mac下MySQL卸载方法 转载
			
mac下mysql的DMG格式安装内有安装文件,却没有卸载文件……很郁闷的事. 网上搜了一下,发现给的方法原来得手动去删. 很多文章记述要删的文件不完整,后来在stackoverflow这里发现了一个 ...
 - 云计算相关的一些概念Baas、Saas、Iaas、Paas
			
BaaS(后端即服务:Backend as a Service)公司为移动应用开发者提供整合云后端的边界服务. SaaS(软件即服务:Software as a Service)提供了完整的可直接使用 ...
 - android 开发 - 网络图片加载库 Fresco 的使用。
			
概述 Fresco 是 facebook 的开源类库,它支持更有效的加载网络图片以及资源图片.它自带三级缓存功能,让图片显示更高效. 介绍 Fresco 是一个强大的图片加载组件. Fresco 中设 ...
 - 分布式并行数据库将在OLTP 领域促进去“Oracle”
			
原文链接:http://www.csdn.net/article/2015-09-11/2825678 摘要:本文全面介绍了分布式数据库和它的设计理念,以及分布式数据库的优势和应用场景,从而引出OLT ...
 - 重构第1天:封装集合(Encapsulate Collection)
			
理解:封装集合就是把集合进行封装,只提供调用者所需要的功能行借口,保证集合的安全性. 详解:在大多的时候,我们没有必要把所有的操作暴露给调用者,只需要把调用者需要的相关操作暴露给他,这种情况中下我们就 ...
 - 译:用InnoSetup模块化安装依赖项
			
译文出处:http://www.codeproject.com/Articles/20868/NET-Framework-Installer-for-InnoSetup 源文件下载:http://fi ...
 - 2012年中国科学技术信息研究所发布自然科学SCI(E)论文期刊分区列表
			
分区 期刊名称 ISSN 学科名称 学科名称(中文) 总被引频次 影响因子 5年影响因子 即年影响因子 被引半衰期 特征因子 论文影响指标 1区 ACM COMPUT SURV 0360-0300 C ...
 - IOS中多版本,多设备类型支持注意事项
			
IOS系统从07年出来,到现在也有6年了,每年发布一次到两次新的设备,从iPhone1,iPhone2 ... iPhone4s再到最新的iPhone5.硬件在升级的过程中CPU的架构也可能发生变化, ...