CS
中缓存对性能的优化起了非常大的作用,今天做一次深入的研究。经过大致的代码浏览发现CS中的缓存分为2种:一种采用System.Web.Caching,另一种采用HttpContext.Items(由于CS大量的采用服务器端控件没有使用页面级的缓存)。
首先研究一下System.web.Caching.Cache的使用
在
CommunityServerComponents项目中发现了CommunityServer.Components.CSCache,查看一下代码
private
CSCache(){}
//>> Based on Factor = 5 default value
public
static
readonly
int
DayFactor = 17280;
public
static
readonly
int
HourFactor = 720;
public
static
readonly
int
MinuteFactor = 12;
private
static
readonly
Cache _cache;
private
static
int
Factor = 5;
public
static
void
ReSetFactor(
int
cacheFactor)
{
Factor = cacheFactor;
}
///
///
Static initializer should ensure we only have to look up the current cache
///
instance once.
///
static
CSCache()
{
HttpContext context = HttpContext.Current;
if
(context !=
null
)
{
_cache = context.Cache;
}
else
{
_cache = HttpRuntime.Cache;
}
}
发现其实是对
System.Web.Caching.Cache作了一个封装(在CS中这样的例子比比皆是如:CSContext),主要实现了一下功能:
1
、插入:Insert、MicroInsert(插入生存周期短的缓存项目)、Max(插入生存周期很长的缓存项目,在系统运行期间永久缓存)
2、获取:Get
3、移除:Remove、RemoveByPattern
4、全部清除:Clear
另外大家看一下“ private static int Factor = 5;” ,如果希望不用缓存则直接设置为Factor=0即可,如果希望延长缓存生存周期则适当调大Factor值,也可以在CommunityServer.config中修改“cacheFactor”的数值。
2、获取:Get
3、移除:Remove、RemoveByPattern
4、全部清除:Clear
另外大家看一下“ private static int Factor = 5;” ,如果希望不用缓存则直接设置为Factor=0即可,如果希望延长缓存生存周期则适当调大Factor值,也可以在CommunityServer.config中修改“cacheFactor”的数值。
在解决方案中搜索一下“CSCache”,会发现有63个文件,分析一下缓存内容主要有2种类型:配置文件及从数据库读取的实体。
一个是配置文件信息(例如:CommunityServer.Configuration.CSConfiguration、CommunityServer.Galleries.Components.GalleryConfiguration、CommunityServer.Blogs.Components.WeblogConfiguration等)
例如CommunityServer.Configuration.CSConfiguration
例如CommunityServer.Configuration.CSConfiguration
public
static
CSConfiguration GetConfig()
{
CSConfiguration config = CSCache.Get(CacheKey)
as
CSConfiguration;
if
(config ==
null
)
{
string
path;
if
(HttpContext.Current !=
null
)
path = HttpContext.Current.Server.MapPath("~/communityserver.config");
else
path = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "communityserver.config";
XmlDocument doc =
new
XmlDocument();
doc.Load(path);
config =
new
CSConfiguration(doc);
CSCache.Max(CacheKey,config,new CacheDependency(path));
CSCache.ReSetFactor(config.CacheFactor);
}
return
config;
}
public
static
CSConfiguration GetConfig()
{
CSConfiguration config = CSCache.Get(CacheKey)
as
CSConfiguration;
if
(config ==
null
)
{
string
path;
if
(HttpContext.Current !=
null
)
path = HttpContext.Current.Server.MapPath("~/communityserver.config");
else
path = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "communityserver.config";
XmlDocument doc =
new
XmlDocument();
doc.Load(path);
config =
new
CSConfiguration(doc);
CSCache.Max(CacheKey,config,new CacheDependency(path));
CSCache.ReSetFactor(config.CacheFactor);
}
return
config;
}
可以看到对communityserver.config的配置信息在第一次使用时从配置文件中读取出来并加入采用CSCache.Max()永久缓存,其失效策略为communityserver.config文件的更改。回顾一下,System.Web.Caching.Cache的失效策略一般有三种情况:文件的更改、缓存中其他缓存内容的更改、定义失效时间。GalleryConfiguration及WeblogConfiguration内的缓存失效策略是第2种情况的一个示例
public
static
WeblogConfiguration Instance()
{
string
cacheKey = "WeblogConfiguration";
WeblogConfiguration config = CSCache.Get(cacheKey)
as
WeblogConfiguration;
if
(config ==
null
)
{
XmlNode node = CSConfiguration.GetConfig().GetConfigSection("CommunityServer/Weblog");
config =
new
WeblogConfiguration();
...
CacheDependency dep
=
new CacheDependency(null, newstring[]);
CSCache.Insert(cacheKey, config, dep);
}
return
config;
}
另一种是数据库实体的缓存,以CommunityServer.Discussions.Components.Posts为例
public
static
PostSet GetPosts(
int
postID,
int
pageIndex,
int
pageSize,
int
sortBy,
int
sortOrder)
{
PostSet postSet;
CSContext csContext = CSContext.Current;
string key = "Forum-Posts::P:-PI:-PS:-SB:-SO:";
string postCollectionKey = string.Format(key,postID,pageIndex,pageSize, sortBy, sortOrder);
// Attempt to retrieve from Cache
postSet = CSCache.Get(postCollectionKey)
as
PostSet;
// forumContext.Context.Cache[postCollectionKey];
if
(postSet ==
null
) {
// Create Instance of the CommonDataProvider
ForumDataProvider dp = ForumDataProvider.Instance();
postSet = dp.GetPosts(postID, pageIndex, pageSize, sortBy, sortOrder, CSContext.Current.User.UserID,
true
);
CSCache.Insert(postCollectionKey,postSet,6);
}
return
postSet;
}
GetPosts()
首先从缓存中读取postSet = CSCache.Get(postCollectionKey)
as
PostSet;
如果缓存中不存在则从数据库中读取并放入缓存(缓存失效策略为定义的时间段),这是CS减少数据库连接次数最有效的方式。另外其缓存key值的设置规则也是非常值得学习的。
如果缓存中不存在则从数据库中读取并放入缓存(缓存失效策略为定义的时间段),这是CS减少数据库连接次数最有效的方式。另外其缓存key值的设置规则也是非常值得学习的。
然后研究一下HttpContext.Items的使用
public
static
WeblogConfiguration Instance()
{
string
cacheKey = "WeblogConfiguration";
WeblogConfiguration config = CSCache.Get(cacheKey)
as
WeblogConfiguration;
if
(config ==
null
)
{
XmlNode node = CSConfiguration.GetConfig().GetConfigSection("CommunityServer/Weblog");
config =
new
WeblogConfiguration();
...
CacheDependency dep
=
new CacheDependency(null, newstring[]);
CSCache.Insert(cacheKey, config, dep);
}
return
config;
}
另一种是数据库实体的缓存,以
CommunityServer.Discussions.Components.Posts为例
public
static
PostSet GetPosts(
int
postID,
int
pageIndex,
int
pageSize,
int
sortBy,
int
sortOrder)
{
PostSet postSet;
CSContext csContext = CSContext.Current;
string key = "Forum-Posts::P:-PI:-PS:-SB:-SO:";
string postCollectionKey = string.Format(key,postID,pageIndex,pageSize, sortBy, sortOrder);
// Attempt to retrieve from Cache
postSet = CSCache.Get(postCollectionKey)
as
PostSet;
// forumContext.Context.Cache[postCollectionKey];
if
(postSet ==
null
) {
// Create Instance of the CommonDataProvider
ForumDataProvider dp = ForumDataProvider.Instance();
postSet = dp.GetPosts(postID, pageIndex, pageSize, sortBy, sortOrder, CSContext.Current.User.UserID,
true
);
CSCache.Insert(postCollectionKey,postSet,6);
}
return
postSet;
}
GetPosts()首先从缓存中读取
postSet = CSCache.Get(postCollectionKey) as PostSet;
如果缓存中不存在则从数据库中读取并放入缓存(缓存失效策略为定义的时间段),这是CS减少数据库连接次数最有效的方式。另外其缓存key值的设置规则也是非常值得学习的。
如果缓存中不存在则从数据库中读取并放入缓存(缓存失效策略为定义的时间段),这是CS减少数据库连接次数最有效的方式。另外其缓存key值的设置规则也是非常值得学习的。
然后研究一下HttpContext.Items的使用
然后研究一下HttpContext.Items的使用
HttpContext.Items
被Rob Howard称之为“每请求缓存”(参见编写高性能 Web 应用程序的 10 个技巧),故名思义即缓存只存在于HttpRequest请求期间,请求结束后缓存即失效。
CS中采用CSContext对HttpContext作了封装,搜索一下“CSContext.Items”发现有7个文件。以CommunityServer.Discussions.Components.Forums为例,分析一下代码
CS中采用CSContext对HttpContext作了封装,搜索一下“CSContext.Items”发现有7个文件。以CommunityServer.Discussions.Components.Forums为例,分析一下代码
private
static
Hashtable GetForums(CSContext csContext,
bool
ignorePermissions,
bool
cacheable,
bool
flush) {
Hashtable unfilteredForums =
null
;
Hashtable forums;
int
settingsID = CSContext.Current.SiteSettings.SettingsID;
string
cacheKey =
string
.Format("Forums-Site:",settingsID);
string
localKey =
string
.Format("ForumsForUser:",ignorePermissions);
if
(flush)
{
CSCache.Remove(cacheKey);
csContext.Items[localKey] =
null
;
}
#if
DEBUG_NOCACHE
cacheable =
false
;
#endif
// Have we already fetched for this request?
//
//If something is in the context with the current cache key, we have already processed and validated this request!
unfilteredForums = csContext.Items[localKey]
as
Hashtable;
//We do not need to revalidate this collection on the same request
if
((unfilteredForums !=
null
))
return
unfilteredForums;
else
if
(!cacheable)
csContext.Items.Remove(cacheKey);
//Is it safe to use the cached version?
if
((!cacheable))
CSCache.Remove(cacheKey);
//If we find the forums in the cache, we need to revalidate them. DO NOT return the coolection unless
//the call specifies ignorepermissions
unfilteredForums = CSCache.Get(cacheKey)
as
Hashtable;
// Get the raw forum groups
//
if
( unfilteredForums ==
null
) {
unfilteredForums = ForumDataProvider.Instance().GetForums();
// Dynamically add the special forum for private messages
//
unfilteredForums.Add( 0, PrivateForum() );
// Cache if we can
//
if
(cacheable)
CSCache.Insert(cacheKey,unfilteredForums,CSCache.MinuteFactor * 15,CacheItemPriority.High);
}
// Are we ignoring permissions?
//
if
(ignorePermissions)
{
//Save us the logic look up later
csContext[localKey] = unfilteredForums;
return
unfilteredForums;
}
// We need to create a new hashtable
//
forums =
new
Hashtable();
User user = CSContext.Current.User;
// Filter the list of forums to only show forums this user
// is allowed to see
//
foreach
(Forum f
in
unfilteredForums.Values) {
// The forum is added if the user can View, Read
//
if
( Permissions.ValidatePermissions(f,Permission.View,user) )
if
(f.IsActive || user.IsForumAdministrator)
forums.Add(f.SectionID, f);
}
// Insert into request cache
//
csContext[localKey] = forums;
return
forums;
}
第一次访问时首先从数据库中读取数据并存储到Cache及HttpContext.Items中,但是请求结束后HttpContext.Items的缓存即可消失,到底能起什么作用呢?搜索GetForums()发现有多个服务器端控件使用这个方法,如果有多个其中的控件出现在一个页面即一个请求中时,则缓存就发生作用了
private
static
Hashtable GetForums(CSContext csContext,
bool
ignorePermissions,
bool
cacheable,
bool
flush) {
Hashtable unfilteredForums =
null
;
Hashtable forums;
int
settingsID = CSContext.Current.SiteSettings.SettingsID;
string
cacheKey =
string
.Format("Forums-Site:",settingsID);
string
localKey =
string
.Format("ForumsForUser:",ignorePermissions);
if
(flush)
{
CSCache.Remove(cacheKey);
csContext.Items[localKey] =
null
;
}
#if
DEBUG_NOCACHE
cacheable =
false
;
#endif
// Have we already fetched for this request?
//
//If something is in the context with the current cache key, we have already processed and validated this request!
unfilteredForums = csContext.Items[localKey]
as
Hashtable;
//We do not need to revalidate this collection on the same request
if
((unfilteredForums !=
null
))
return
unfilteredForums;
else
if
(!cacheable)
csContext.Items.Remove(cacheKey);
//Is it safe to use the cached version?
if
((!cacheable))
CSCache.Remove(cacheKey);
//If we find the forums in the cache, we need to revalidate them. DO NOT return the coolection unless
//the call specifies ignorepermissions
unfilteredForums = CSCache.Get(cacheKey)
as
Hashtable;
// Get the raw forum groups
//
if
( unfilteredForums ==
null
) {
unfilteredForums = ForumDataProvider.Instance().GetForums();
// Dynamically add the special forum for private messages
//
unfilteredForums.Add( 0, PrivateForum() );
// Cache if we can
//
if
(cacheable)
CSCache.Insert(cacheKey,unfilteredForums,CSCache.MinuteFactor * 15,CacheItemPriority.High);
}
// Are we ignoring permissions?
//
if
(ignorePermissions)
{
//Save us the logic look up later
csContext[localKey] = unfilteredForums;
return
unfilteredForums;
}
// We need to create a new hashtable
//
forums =
new
Hashtable();
User user = CSContext.Current.User;
// Filter the list of forums to only show forums this user
// is allowed to see
//
foreach
(Forum f
in
unfilteredForums.Values) {
// The forum is added if the user can View, Read
//
if
( Permissions.ValidatePermissions(f,Permission.View,user) )
if
(f.IsActive || user.IsForumAdministrator)
forums.Add(f.SectionID, f);
}
// Insert into request cache
//
csContext[localKey] = forums;
return
forums;
}
第一次访问时首先从数据库中读取数据并存储到Cache及HttpContext.Items中,但是请求结束后HttpContext.Items的缓存即可消失,到底能起什么作用呢?搜索GetForums()发现有多个服务器端控件使用这个方法,如果有多个其中的控件出现在一个页面即一个请求中时,则缓存就发生作用了