在之前的Discuz!NT缓存的架构方案中,曾说过Discuz!NT采用了两级缓存方式,即
本地缓存+memcached方式。在近半年多的实际运行环境下,该方案经受住了检验。现在为了提供多样式的解决方案,我在企业版里引入了Redis这个目前炙手可热的缓存架构产品,即将memcached与Redis作为可选插件方式来提供了最终用户,尽管目前测试的结果两者的差异不是很大(毫秒级),但我想多一种选择对用户来说也是好的。
闲话不多说了,开始今天的正文吧。
熟悉我们产品的开发者都知道,我们的产品在缓存设计上使用了策略模式(Stratety Pattern),之前在系统中就已实现了DefaultCacheStrategy和MemCachedStrategy,前者用于本地缓存方式,后者顾名思义,就是memcached缓存方式。所以只要我们实现一个RedisStrategy策略,并适当修改加载缓存策略的代码,就可以将memcached缓存方式替换成Redis,如下图:
下面我先将RedisStrategy的部分代码放上来,大家一看便知:
- ///
- /// 企业级Redis缓存策略类
- ///
- public class RedisStrategy : DefaultCacheStrategy
- {
- ///
- /// 添加指定ID的对象
- ///
- ///
- ///
- public override void AddObject(string objId, object o)
- {
- if (!objId.StartsWith("/Forum/ShowTopic/"))
- base.AddObject(objId, o, LocalCacheTime);
- using (IRedisClient Redis = RedisManager.GetClient())
- {
- Redis.Set(objId, new ObjectSerializer().Serialize(o));
- }
- }
- ///
- /// 加入当前对象到缓存中
- ///
- /// 对象的键值
- /// 缓存的对象
- /// 到期时间,单位:秒
- public override void AddObject(string objId, object o, int expire)
- {
- //凡是以"/Forum/ShowTopic/"为前缀不添加到本地缓存中,现类似键值包括: "/Forum/ShowTopic/Tag/{topicid}/" , "/Forum/ShowTopic/TopList/{fid}"
- if (!objId.StartsWith("/Forum/ShowTopic/"))
- base.AddObject(objId, o, expire);
- using (IRedisClient Redis = RedisManager.GetClient())
- {
- //永不过期
- if (expire == 0)
- Redis.Set(objId, new ObjectSerializer().Serialize(o));
- else
- Redis.Set(objId, new ObjectSerializer().Serialize(o), DateTime.Now.AddSeconds(expire));
- }
- }
- ///
- /// 移除指定ID的对象
- ///
- ///
- public override void RemoveObject(string objId)
- {
- //先移除本地cached,然后再移除memcached中的相应数据
- base.RemoveObject(objId);
- using (IRedisClient Redis = RedisManager.GetClient())
- {
- Redis.Remove(objId);
- }
- Discuz.EntLib.SyncCache.SyncRemoteCache(objId);
- }
- public override object RetrieveObject(string objId)
- {
- object obj = base.RetrieveObject(objId);
- if (obj == null)
- {
- using (IRedisClient Redis = RedisManager.GetClient())
- {
- obj = new ObjectSerializer().Deserialize(Redis.Get(objId));
- if (obj != null && !objId.StartsWith("/Forum/ShowTopic/"))//对ShowTopic页面缓存数据不放到本地缓存
- {
- if (objId.StartsWith("/Forum/ShowTopicGuestCachePage/"))//对游客缓存页面ShowTopic数据缓存设置有效时间
- base.TimeOut = GeneralConfigs.GetConfig().Guestcachepagetimeout * 60;
- if (objId.StartsWith("/Forum/ShowForumGuestCachePage/"))//对游客缓存页面ShowTopic数据缓存设置有效时间
- base.TimeOut = RedisConfigs.GetConfig().CacheShowForumCacheTime * 60;
- else
- base.TimeOut = LocalCacheTime;
- base.AddObject(objId, obj, TimeOut);
- }
- }
- }
- return obj;
- }
- ///
- /// 到期时间,单位:秒
- ///
- public override int TimeOut
- {
- get
- {
- return 3600;
- }
- }
- ///
- /// 本地缓存到期时间,单位:秒
- ///
- public int LocalCacheTime
- {
- get
- {
- return RedisConfigs.GetConfig().LocalCacheTime;
- }
- }
- ///
- /// 清空的有缓存数据
- ///
- public override void FlushAll()
- {
- base.FlushAll();
- using (IRedisClient Redis = RedisManager.GetClient())
- {
- Redis.FlushAll();
- }
- }
- }
可以看出RedisStrategy类继承自DefaultCacheStrategy,这一点与MemCachedStrategy实现如出一辙,唯一不同是其缓存数据加载和设置的方式有所不同,而具体的用法可以参见我之前写的这篇文章中的“object序列化方式存储” 。
当然上面代码中有两个类未说明,一个是RedisConfigs,一个是RedisManager,前者是配置文件信息类,我们产品因为使用了统一的序列化接口实现方式(参见该文),所以其实现方式比较清晰,其序列化类的结构如下:
其序列化出来的xml文件格式形如:
- true
- 10.0.4.210:6379
- 10.0.4.210:6379
- 60
- 60
- true
- 180
- false
- 2
- 2
- 10
之前所说的RedisManager类则是一个RedisClient客户端实现的简单封装,主要为了简化基于链接池的Redis链接方式的使用。其结构如下:
上面的代码主要将redis.config的配置文件文件信息加载到程序里并实始化PooledRedisClientManager对象,该对象用于池化redis的客户端链接,具体方式参见这篇文章
好了,到这里主要的内容就介绍完了。
注:本文的部分代码位于企业版产品中,目前暂未开源所以大家可能没有拿到,我们计划今年开源企业版1.0的代码,包括本文中代码部分,以便从社区中获取更多经验和反馈,同时希望大家支持和关注我们的产品。
原文链接:http://www.cnblogs.com/daizhj/archive/2011/02/21/1959511.html
阅读(1173) | 评论(0) | 转发(0) |