Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5096398
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类: 数据库开发技术

2011-03-26 19:56:42

     在之前的Discuz!NT缓存的架构方案中,曾说过Discuz!NT采用了两级缓存方式,即本地缓存+memcached方式。在近半年多的实际运行环境下,该方案经受住了检验。现在为了提供多样式的解决方案,我在企业版里引入了Redis这个目前炙手可热的缓存架构产品,即将memcached与Redis作为可选插件方式来提供了最终用户,尽管目前测试的结果两者的差异不是很大(毫秒级),但我想多一种选择对用户来说也是好的。

     闲话不多说了,开始今天的正文吧。
    
     熟悉我们产品的开发者都知道,我们的产品在缓存设计上使用了策略模式(Stratety Pattern),之前在系统中就已实现了DefaultCacheStrategy和MemCachedStrategy,前者用于本地缓存方式,后者顾名思义,就是memcached缓存方式。所以只要我们实现一个RedisStrategy策略,并适当修改加载缓存策略的代码,就可以将memcached缓存方式替换成Redis,如下图:
 下面我先将RedisStrategy的部分代码放上来,大家一看便知:
  1. ///
  2. /// 企业级Redis缓存策略类
  3. ///
  4. public class RedisStrategy : DefaultCacheStrategy
  5. {
  6. ///
  7. /// 添加指定ID的对象
  8. ///
  9. ///
  10. ///
  11. public override void AddObject(string objId, object o)
  12. {
  13. if (!objId.StartsWith("/Forum/ShowTopic/"))
  14. base.AddObject(objId, o, LocalCacheTime);
  15. using (IRedisClient Redis = RedisManager.GetClient())
  16. {
  17. Redis.Set(objId, new ObjectSerializer().Serialize(o));
  18. }
  19. }
  20. ///
  21. /// 加入当前对象到缓存中
  22. ///
  23. /// 对象的键值
  24. /// 缓存的对象
  25. /// 到期时间,单位:秒
  26. public override void AddObject(string objId, object o, int expire)
  27. {
  28. //凡是以"/Forum/ShowTopic/"为前缀不添加到本地缓存中,现类似键值包括: "/Forum/ShowTopic/Tag/{topicid}/" , "/Forum/ShowTopic/TopList/{fid}"
  29. if (!objId.StartsWith("/Forum/ShowTopic/"))
  30. base.AddObject(objId, o, expire);
  31. using (IRedisClient Redis = RedisManager.GetClient())
  32. {
  33. //永不过期
  34. if (expire == 0)
  35. Redis.Set(objId, new ObjectSerializer().Serialize(o));
  36. else
  37. Redis.Set(objId, new ObjectSerializer().Serialize(o), DateTime.Now.AddSeconds(expire));
  38. }
  39. }
  40. ///
  41. /// 移除指定ID的对象
  42. ///
  43. ///
  44. public override void RemoveObject(string objId)
  45. {
  46. //先移除本地cached,然后再移除memcached中的相应数据
  47. base.RemoveObject(objId);
  48. using (IRedisClient Redis = RedisManager.GetClient())
  49. {
  50. Redis.Remove(objId);
  51. }
  52. Discuz.EntLib.SyncCache.SyncRemoteCache(objId);
  53. }
  54. public override object RetrieveObject(string objId)
  55. {
  56. object obj = base.RetrieveObject(objId);
  57. if (obj == null)
  58. {
  59. using (IRedisClient Redis = RedisManager.GetClient())
  60. {
  61. obj = new ObjectSerializer().Deserialize(Redis.Get(objId));
  62. if (obj != null && !objId.StartsWith("/Forum/ShowTopic/"))//对ShowTopic页面缓存数据不放到本地缓存
  63. {
  64. if (objId.StartsWith("/Forum/ShowTopicGuestCachePage/"))//对游客缓存页面ShowTopic数据缓存设置有效时间
  65. base.TimeOut = GeneralConfigs.GetConfig().Guestcachepagetimeout * 60;
  66. if (objId.StartsWith("/Forum/ShowForumGuestCachePage/"))//对游客缓存页面ShowTopic数据缓存设置有效时间
  67. base.TimeOut = RedisConfigs.GetConfig().CacheShowForumCacheTime * 60;
  68. else
  69. base.TimeOut = LocalCacheTime;
  70. base.AddObject(objId, obj, TimeOut);
  71. }
  72. }
  73. }
  74. return obj;
  75. }
  76. ///
  77. /// 到期时间,单位:秒
  78. ///
  79. public override int TimeOut
  80. {
  81. get
  82. {
  83. return 3600;
  84. }
  85. }
  86. ///
  87. /// 本地缓存到期时间,单位:秒
  88. ///
  89. public int LocalCacheTime
  90. {
  91. get
  92. {
  93. return RedisConfigs.GetConfig().LocalCacheTime;
  94. }
  95. }
  96. ///
  97. /// 清空的有缓存数据
  98. ///
  99. public override void FlushAll()
  100. {
  101. base.FlushAll();
  102. using (IRedisClient Redis = RedisManager.GetClient())
  103. {
  104. Redis.FlushAll();
  105. }
  106. }
  107. }

 

可以看出RedisStrategy类继承自DefaultCacheStrategy,这一点与MemCachedStrategy实现如出一辙,唯一不同是其缓存数据加载和设置的方式有所不同,而具体的用法可以参见我之前写的这篇文章中的“object序列化方式存储”  。
    
     当然上面代码中有两个类未说明,一个是RedisConfigs,一个是RedisManager,前者是配置文件信息类,我们产品因为使用了统一的序列化接口实现方式(参见该文),所以其实现方式比较清晰,其序列化类的结构如下:

 

  1. ///
  2. /// Redis配置信息类文件
  3. ///
  4. public class RedisConfigInfo : IConfigInfo
  5. {
  6. private bool _applyRedis;
  7. ///
  8. /// 是否应用Redis
  9. ///
  10. public bool ApplyRedis
  11. {
  12. get
  13. {
  14. return _applyRedis;
  15. }
  16. set
  17. {
  18. _applyRedis = value;
  19. }
  20. }
  21. private string _writeServerList;
  22. ///
  23. /// 可写的Redis链接地址
  24. ///
  25. public string WriteServerList
  26. {
  27. get
  28. {
  29. return _writeServerList;
  30. }
  31. set
  32. {
  33. _writeServerList = value;
  34. }
  35. }
  36. private string _readServerList;
  37. ///
  38. /// 可读的Redis链接地址
  39. ///
  40. public string ReadServerList
  41. {
  42. get
  43. {
  44. return _readServerList;
  45. }
  46. set
  47. {
  48. _readServerList = value;
  49. }
  50. }
  51. private int _maxWritePoolSize;
  52. ///
  53. /// 最大写链接数
  54. ///
  55. public int MaxWritePoolSize
  56. {
  57. get
  58. {
  59. return _maxWritePoolSize > 0 ? _maxWritePoolSize : 5;
  60. }
  61. set
  62. {
  63. _maxWritePoolSize = value;
  64. }
  65. }
  66. private int _maxReadPoolSize;
  67. ///
  68. /// 最大读链接数
  69. ///
  70. public int MaxReadPoolSize
  71. {
  72. get
  73. {
  74. return _maxReadPoolSize > 0 ? _maxReadPoolSize : 5;
  75. }
  76. set
  77. {
  78. _maxReadPoolSize = value;
  79. }
  80. }
  81. private bool _autoStart;
  82. ///
  83. /// 自动重启
  84. ///
  85. public bool AutoStart
  86. {
  87. get
  88. {
  89. return _autoStart;
  90. }
  91. set
  92. {
  93. _autoStart = value;
  94. }
  95. }
  96. private int _localCacheTime = 30000;
  97. ///
  98. /// 本地缓存到期时间,该设置会与memcached搭配使用,单位:秒
  99. ///
  100. public int LocalCacheTime
  101. {
  102. get
  103. {
  104. return _localCacheTime;
  105. }
  106. set
  107. {
  108. _localCacheTime = value;
  109. }
  110. }
  111. private bool _recordeLog = false;
  112. ///
  113. /// 是否记录日志,该设置仅用于排查redis运行时出现的问题,如redis工作正常,请关闭该项
  114. ///
  115. public bool RecordeLog
  116. {
  117. get
  118. {
  119. return _recordeLog;
  120. }
  121. set
  122. {
  123. _recordeLog = value;
  124. }
  125. }
  126. private int _cacheShowTopicPageNumber = 5;
  127. ///
  128. /// 缓存帖子列表分页数(showtopic页数使用缓存前N页的帖子列表信息)
  129. ///
  130. public int CacheShowTopicPageNumber
  131. {
  132. get
  133. {
  134. return _cacheShowTopicPageNumber;
  135. }
  136. set
  137. {
  138. _cacheShowTopicPageNumber = value;
  139. }
  140. }
  141. ///
  142. /// 缓存showforum页面分页数
  143. ///
  144. public int CacheShowForumPageNumber{set;get;}
  145. ///
  146. /// 缓存showforum页面时间(单位:分钟)
  147. ///
  148. public int CacheShowForumCacheTime{set;get;}
  149. }

 

其序列化出来的xml文件格式形如:

 

  1. true
  2. 10.0.4.210:6379
  3. 10.0.4.210:6379
  4. 60
  5. 60
  6. true
  7. 180
  8. false
  9. 2
  10. 2
  11. 10

 之前所说的RedisManager类则是一个RedisClient客户端实现的简单封装,主要为了简化基于链接池的Redis链接方式的使用。其结构如下:

  1. using System.Collections;
  2. using Discuz.Config;
  3. using Discuz.Common;
  4. using ServiceStack.Redis;
  5. using ServiceStack.Redis.Generic;
  6. using ServiceStack.Redis.Support;
  7. namespace Discuz.EntLib
  8. {
  9. ///
  10. /// MemCache管理操作类
  11. ///
  12. public sealed class RedisManager
  13. {
  14. ///
  15. /// redis配置文件信息
  16. ///
  17. private static RedisConfigInfo redisConfigInfo = RedisConfigs.GetConfig();
  18. private static PooledRedisClientManager prcm;
  19. ///
  20. /// 静态构造方法,初始化链接池管理对象
  21. ///
  22. static RedisManager()
  23. {
  24. CreateManager();
  25. }
  26. ///
  27. /// 创建链接池管理对象
  28. ///
  29. private static void CreateManager()
  30. {
  31. string[] writeServerList = Utils.SplitString(redisConfigInfo.WriteServerList, ",");
  32. string[] readServerList = Utils.SplitString(redisConfigInfo.ReadServerList, ",");
  33. prcm = new PooledRedisClientManager(readServerList, writeServerList,
  34. new RedisClientManagerConfig
  35. {
  36. MaxWritePoolSize = redisConfigInfo.MaxWritePoolSize,
  37. MaxReadPoolSize = redisConfigInfo.MaxReadPoolSize,
  38. AutoStart = redisConfigInfo.AutoStart,
  39. });
  40. }
  41. ///
  42. /// 客户端缓存操作对象
  43. ///
  44. public static IRedisClient GetClient()
  45. {
  46. if (prcm == null)
  47. CreateManager();
  48. return prcm.GetClient();
  49. }
  50. }
  51. }

 

     上面的代码主要将redis.config的配置文件文件信息加载到程序里并实始化PooledRedisClientManager对象,该对象用于池化redis的客户端链接,具体方式参见这篇文章
       
      好了,到这里主要的内容就介绍完了。
     
      注:本文的部分代码位于企业版产品中,目前暂未开源所以大家可能没有拿到,我们计划今年开源企业版1.0的代码,包括本文中代码部分,以便从社区中获取更多经验和反馈,同时希望大家支持和关注我们的产品。
   

      原文链接:http://www.cnblogs.com/daizhj/archive/2011/02/21/1959511.html

      作者: daizhj, 代震军

阅读(1122) | 评论(0) | 转发(0) |
0

上一篇:redis资料汇总

下一篇:二.redis 数据类型

给主人留下些什么吧!~~