【转】系统缓存全解析二:动态缓存(4)-Discuz!NT中集成Memcached分布式缓存

Discuz!NT中集成Memcached分布式缓存
文章出处:DIY部落(http://www.diybl.com/course/3_program/cshapo/csharpsl/20100112/189244.html)

其实在之前的那篇关于Discuz!NT缓存架构的文章中已提到过,使用了设计模式中的“策略模式”来构造。所以为了与以往使用缓存的代码格式相兼容,所以这里采用新添加MemCachedStrategy(MemCached策略)来构造一个缓存策略类以便于当管理后台开启“MemCached”时以“MemCached策略模式”来做为当前系统默认
的策略模式。

       其代码段如下(Discuz.Cache/MemCached.cs):

ExpandedBlockStart.gif View Code
 1  ///   <summary>
 2  ///  MemCache缓存策略类
 3  ///   </summary>
 4  public  class MemCachedStrategy : Discuz.Cache.ICacheStrategy
 5 {
 6 
 7      ///   <summary>
 8       ///  添加指定ID的对象
 9       ///   </summary>
10       ///   <param name="objId"></param>
11       ///   <param name="o"></param>
12       public  void AddObject( string objId,  object o)
13     {
14         RemoveObject(objId);
15          if (TimeOut >  0)
16         {
17             MemCachedManager.CacheClient.Set(objId, o, System.DateTime.Now.AddMinutes(TimeOut));
18         }
19          else
20         {
21             MemCachedManager.CacheClient.Set(objId, o);
22         }
23     }
24 
25      ///   <summary>
26       ///  添加指定ID的对象(关联指定文件组)
27       ///   </summary>
28       ///   <param name="objId"></param>
29       ///   <param name="o"></param>
30       ///   <param name="files"></param>
31       public  void AddObjectWithFileChange( string objId,  object o,  string[] files)
32     {
33         ;
34     }
35 
36      ///   <summary>
37       ///  添加指定ID的对象(关联指定键值组)
38       ///   </summary>
39       ///   <param name="objId"></param>
40       ///   <param name="o"></param>
41       ///   <param name="dependKey"></param>
42       public  void AddObjectWithDepend( string objId,  object o,  string[] dependKey)
43     {
44         ;
45     }
46 
47      ///   <summary>
48       ///  移除指定ID的对象
49       ///   </summary>
50       ///   <param name="objId"></param>
51       public  void RemoveObject( string objId)
52     {
53          if (MemCachedManager.CacheClient.KeyExists(objId))
54             MemCachedManager.CacheClient.Delete(objId);
55     }
56 
57      ///   <summary>
58       ///  返回指定ID的对象
59       ///   </summary>
60       ///   <param name="objId"></param>
61       ///   <returns></returns>
62       public  object RetrieveObject( string objId)
63     {
64          return MemCachedManager.CacheClient.Get(objId);
65     }
66 
67      ///   <summary>
68       ///  到期时间
69       ///   </summary>
70       public  int TimeOut {  setget; }
71 }

 

       上面类实现的接口Discuz.Cache.ICacheStrategy定义如下: 

ExpandedBlockStart.gif View Code
 1  ///   <summary>
 2  ///  公共缓存策略接口
 3  ///   </summary>
 4  public  interface ICacheStrategy
 5 {
 6       ///   <summary>
 7        ///  添加指定ID的对象
 8        ///   </summary>
 9        ///   <param name="objId"></param>
10        ///   <param name="o"></param>
11        void AddObject( string objId,  object o);
12 
13       ///   <summary>
14        ///  添加指定ID的对象(关联指定文件组)
15        ///   </summary>
16        ///   <param name="objId"></param>
17        ///   <param name="o"></param>
18        ///   <param name="files"></param>
19        void AddObjectWithFileChange( string objId,  object o,  string[] files);
20 
21       ///   <summary>
22        ///  添加指定ID的对象(关联指定键值组)
23        ///   </summary>
24        ///   <param name="objId"></param>
25        ///   <param name="o"></param>
26        ///   <param name="dependKey"></param>
27        void AddObjectWithDepend( string objId,  object o,  string[] dependKey);
28 
29       ///   <summary>
30        ///  移除指定ID的对象
31         ///   </summary>
32        ///   <param name="objId"></param>
33        void RemoveObject( string objId);
34 
35       ///   <summary>
36        ///  返回指定ID的对象
37         ///   </summary>
38        ///   <param name="objId"></param>
39        ///   <returns></returns>
40        object RetrieveObject( string objId);
41 
42       ///   <summary>
43        ///  到期时间
44         ///   </summary>
45        int TimeOut {  set; get;}
46 }

 

      当然在MemCachedStrategy类中还有一个对象要加以说明,就是MemCachedManager,该类主要是对Memcached一些常操作和相关初始化实例调用的“封装”,下面是是其变量定义和初始化构造方法的代码: 

ExpandedBlockStart.gif View Code
 1  ///   <summary>
 2  ///  MemCache管理操作类
 3  ///   </summary>
 4  public  sealed  class MemCachedManager
 5 {
 6      #region 静态方法和属性
 7      private  static MemcachedClient mc =  null;
 8 
 9      private  static SockIOPool pool =  null;
10 
11      private  static MemCachedConfigInfo memCachedConfigInfo = MemCachedConfigs.GetConfig();
12 
13      private  static  string [] serverList =  null;
14 
15      static MemCachedManager()
16     {
17         CreateManager();
18     }
19 
20      private  static  void CreateManager()
21     {
22         serverList = Utils.SplitString(memCachedConfigInfo.ServerList,  ""r " n ");
23 
24         pool = SockIOPool.GetInstance(memCachedConfigInfo.PoolName);
25         pool.SetServers(serverList);
26         pool.InitConnections = memCachedConfigInfo.IntConnections; // 初始化链接数
27          pool.MinConnections = memCachedConfigInfo.MinConnections; // 最少链接数
28          pool.MaxConnections = memCachedConfigInfo.MaxConnections; // 最大连接数
29          pool.SocketConnectTimeout = memCachedConfigInfo.SocketConnectTimeout; // Socket链接超时时间
30          pool.SocketTimeout = memCachedConfigInfo.SocketTimeout; //  Socket超时时间
31 
32 pool.MaintenanceSleep = memCachedConfigInfo.MaintenanceSleep; // 维护线程休息时间
33          pool.Failover = memCachedConfigInfo.FailOver;  // 失效转移(一种备份操作模式)
34          pool.Nagle = memCachedConfigInfo.Nagle; // 是否用nagle算法启动socket
35          pool.HashingAlgorithm = HashingAlgorithm.NewCompatibleHash;
36         pool.Initialize();
37        
38 
39         mc =  new MemcachedClient();
40         mc.PoolName = memCachedConfigInfo.PoolName;
41         mc.EnableCompression =  false;
42     }
43 
44      ///   <summary>
45       ///  缓存服务器地址列表
46       ///   </summary>
47       public  static  string[] ServerList
48     {
49          set
50         {
51              if (value !=  null)
52                 serverList = value;
53         }
54          get {  return serverList; }
55     }
56 
57      ///   <summary>
58       ///  客户端缓存操作对象
59       ///   </summary>
60       public  static MemcachedClient CacheClient
61     {
62          get
63         {
64              if (mc ==  null)
65                 CreateManager();
66 
67              return mc;
68         }
69     }
70 
71      public  static  void Dispose()
72     {
73          if (pool !=  null)
74             pool.Shutdown();
75     }

 

      上面代码中构造方法会初始化一个池来管理执行Socket链接,并提供静态属性CacheClient以便MemCachedStrategy来调用。

      当然我还在这个管理操作类中添加了几个方法分别用于检测当前有效的分布式缓存服务器的列表,向指定(或全部)缓存服务器发送特定stats命令来获取当前缓存服务器上的数据信息和内存分配信息等,相应的方法如下(详情见注释):

 

ExpandedBlockStart.gif View Code
  1  ///   <summary>
  2  ///  获取当前缓存键值所存储在的服务器
  3  ///   </summary>
  4  ///   <param name="key"> 当前缓存键 </param>
  5  ///   <returns> 当前缓存键值所存储在的服务器 </returns>
  6  public  static  string GetSocketHost( string key)
  7 {
  8      string hostName =  "";
  9     SockIO sock =  null;
 10      try
 11     {
 12         sock = SockIOPool.GetInstance(memCachedConfigInfo.PoolName).GetSock(key);
 13          if (sock !=  null)
 14         {
 15             hostName = sock.Host;
 16         }
 17     }
 18      finally
 19     {
 20          if (sock !=  null)
 21             sock.Close();
 22     }
 23      return hostName;
 24 }
 25 
 26 
 27  ///   <summary>
 28  ///  获取有效的服务器地址
 29  ///   </summary>
 30  ///   <returns> 有效的服务器地 </returns>
 31  public  static  string[] GetConnectedSocketHost()
 32 {
 33     SockIO sock =  null;
 34      string connectedHost =  null;
 35      foreach ( string hostName  in serverList)
 36     {
 37          if (!Discuz.Common.Utils.StrIsNullOrEmpty(hostName))
 38         {
 39              try
 40             {
 41                 sock = SockIOPool.GetInstance(memCachedConfigInfo.PoolName).GetConnection(hostName);
 42                  if (sock !=  null)
 43                 {
 44                     connectedHost = Discuz.Common.Utils.MergeString(hostName, connectedHost);
 45                 }
 46             }
 47              finally
 48             {
 49                  if (sock !=  null)
 50                     sock.Close();
 51             }
 52         }
 53     }
 54      return Discuz.Common.Utils.SplitString(connectedHost,  " , ");
 55 }
 56 
 57  ///   <summary>
 58  ///  获取服务器端缓存的数据信息
 59  ///   </summary>
 60  ///   <returns> 返回信息 </returns>
 61  public  static ArrayList GetStats()
 62 {
 63     ArrayList arrayList =  new ArrayList();
 64      foreach ( string server  in serverList)
 65     {
 66         arrayList.Add(server);
 67     }
 68      return GetStats(arrayList, Stats.Default,  null);
 69 }
 70 
 71  ///   <summary>
 72  ///  获取服务器端缓存的数据信息
 73  ///   </summary>
 74  ///   <param name="serverArrayList"> 要访问的服务列表 </param>
 75  ///   <returns> 返回信息 </returns>
 76  public  static ArrayList GetStats(ArrayList serverArrayList, Stats statsCommand,  string param)
 77 {
 78     ArrayList statsArray =  new ArrayList();
 79     param =Utils.StrIsNullOrEmpty(param)? "":param.Trim().ToLower();
 80 
 81      string commandstr =  " stats ";
 82      // 转换stats命令参数
 83       switch (statsCommand)
 84     {
 85          case Stats.Reset: { commandstr =  " stats reset "break; }
 86          case Stats.Malloc: { commandstr =  " stats malloc "break; }
 87          case Stats.Maps: { commandstr =  " stats maps "break; }
 88          case Stats.Sizes: { commandstr =  " stats sizes "break; }
 89          case Stats.Slabs: { commandstr =  " stats slabs "break; }
 90          case Stats.Items: { commandstr =  " stats "break; }
 91          case Stats.CachedDump:
 92         {
 93              string[] statsparams = Utils.SplitString(param,  "   ");
 94              if(statsparams.Length ==  2)
 95                  if(Utils.IsNumericArray(statsparams))
 96                     commandstr =  " stats cachedump  " + param;
 97 
 98              break;                     
 99         }
100          case Stats.Detail:
101             {
102                  if( string.Equals(param,  " on ") ||  string.Equals(param,  " off ") ||  string.Equals(param,  " dump "))
103                     commandstr =  " stats detail  " + param.Trim();
104 
105                  break;
106             }
107          default: { commandstr =  " stats "break; }
108     }
109      // 加载返回值
110      Hashtable stats = MemCachedManager.CacheClient.Stats(serverArrayList, commandstr);
111      foreach ( string key  in stats.Keys)
112     {
113         statsArray.Add(key);
114         Hashtable values = (Hashtable)stats[key];
115          foreach ( string key2  in values.Keys)
116         {
117             statsArray.Add(key2 +  " : " + values[key2]);
118         }
119     }
120      return statsArray;
121 }
122 
123  ///   <summary>
124  ///  Stats命令行参数
125  ///   </summary>
126  public  enum Stats
127 {
128      ///   <summary>
129       ///  stats : 显示服务器信息, 统计数据等
130       ///   </summary>
131      Default =  0,
132      ///   <summary>
133       ///  stats reset : 清空统计数据
134       ///   </summary>
135      Reset =  1,
136      ///   <summary>
137       ///  stats malloc : 显示内存分配数据
138       ///   </summary>
139      Malloc =  2,
140      ///   <summary>
141       ///  stats maps : 显示"/proc/self/maps"数据
142       ///   </summary>
143      Maps = 3,
144      ///   <summary>
145       ///  stats sizes
146       ///   </summary>
147      Sizes =  4,
148      ///   <summary>
149       ///  stats slabs : 显示各个slab的信息,包括chunk的大小,数目,使用情况等
150       ///   </summary>
151      Slabs =  5,
152      ///   <summary>
153  ///  stats items : 显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)
154       ///   </summary>
155      Items =  6,
156      ///   <summary>
157       ///  stats cachedump slab_id limit_num : 显示某个slab中的前 limit_num 个 key 列表
158       ///   </summary>
159      CachedDump = 7,
160      ///   <summary>
161       ///  stats detail [on|off|dump] : 设置或者显示详细操作记录   on:打开详细操作记录 off:关闭详细操作记录 dump: 显示详细操作记录(每一个键值get,set,hit,del的次数)
162       ///   </summary>
163      Detail =  8
164 }

 

     当然在配置初始化缓存链接池时使用了配置文件方式(memcached.config)来管理相关参数,其info信息类说明如下(Discuz.Config/MemCachedConfigInfo.cs):

 

ExpandedBlockStart.gif View Code
  1  ///   <summary>
  2  ///  MemCached配置信息类文件
  3  ///   </summary>
  4  public  class MemCachedConfigInfo : IConfigInfo
  5 {
  6      private  bool _applyMemCached;
  7      ///   <summary>
  8       ///  是否应用MemCached
  9       ///   </summary>
 10       public  bool ApplyMemCached
 11     {
 12          get
 13         {
 14              return _applyMemCached;
 15         }
 16          set
 17         {
 18             _applyMemCached = value;
 19         }
 20     }
 21 
 22      private  string _serverList;
 23      ///   <summary>
 24       ///  链接地址
 25       ///   </summary>
 26       public  string ServerList
 27     {
 28          get
 29         {
 30              return _serverList;
 31         }
 32          set
 33         {
 34             _serverList = value;
 35         }
 36     }
 37 
 38      private  string _poolName;
 39      ///   <summary>
 40       ///  链接池名称
 41       ///   </summary>
 42       public  string PoolName
 43     {
 44          get
 45         {
 46              return Utils.StrIsNullOrEmpty(_poolName) ?  " DiscuzNT_MemCache " : _poolName;
 47         }
 48          set
 49         {
 50             _poolName = value;
 51         }
 52     }
 53 
 54      private  int _intConnections;
 55      ///   <summary>
 56       ///  初始化链接数
 57       ///   </summary>
 58       public  int IntConnections
 59     {
 60          get
 61         {
 62              return _intConnections >  0 ? _intConnections :  3;
 63         }
 64          set
 65         {
 66             _intConnections = value;
 67         }
 68     }
 69 
 70      private  int _minConnections;
 71      ///   <summary>
 72       ///  最少链接数
 73       ///   </summary>
 74       public  int MinConnections
 75     {
 76          get
 77         {
 78              return _minConnections >  0 ? _minConnections :  3;
 79         }
 80          set
 81         {
 82             _minConnections = value;
 83         }
 84     }
 85 
 86      private  int _maxConnections;
 87      ///   <summary>
 88       ///  最大连接数
 89       ///   </summary>
 90       public  int MaxConnections
 91     {
 92          get
 93         {
 94              return _maxConnections >  0 ?_maxConnections :  5;
 95         }
 96          set
 97         {
 98             _maxConnections = value;
 99         }
100     }
101 
102      private  int _socketConnectTimeout;
103      ///   <summary>
104       ///  Socket链接超时时间
105       ///   </summary>
106       public  int SocketConnectTimeout
107     {
108          get
109         {
110              return _socketConnectTimeout >  1000 ? _socketConnectTimeout :  1000;
111         }
112          set
113         {
114             _socketConnectTimeout = value;
115         }
116     }
117 
118      private  int _socketTimeout;
119      ///   <summary>
120       ///  socket超时时间
121       ///   </summary>
122       public  int SocketTimeout
123     {
124          get
125         {
126              return _socketTimeout >  1000 ? _maintenanceSleep :  3000;
127         }
128          set
129         {
130             _socketTimeout = value;
131         }
132     }
133 
134      private  int _maintenanceSleep;
135      ///   <summary>
136       ///  维护线程休息时间
137       ///   </summary>
138       public  int MaintenanceSleep
139     {
140          get
141         {
142              return _maintenanceSleep >  0 ? _maintenanceSleep :  30;
143         }
144          set
145         {
146             _maintenanceSleep = value;
147         }
148     }
149 
150      private  bool _failOver;
151      ///   <summary>
152       ///  链接失败后是否重启,详情参见[url] http://baike.baidu.com/view/1084309.htm [/url]
153       ///   </summary>
154       public  bool FailOver
155     {
156          get
157         {
158              return;_failOver;
159         }
160          set
161         {
162             _failOver = value;
163         }
164     }
165 
166      private  bool _nagle;
167      ///   <summary>
168       ///  是否用nagle算法启动socket
169       ///   </summary>
170       public  bool Nagle
171     {
172          get
173         {
174              return _nagle;
175         }
176          set
177         {
178             _nagle = value;
179         }
180     }
181 }     

 

      这些参数我们通过注释应该有一些了解,可以说memcached的主要性能都是通过这些参数来决定的,大家应根据自己公司产品和应用的实际情况配置相应的数值。

      当然,做完这一步之后就是对调用“缓存策略”的主体类进行修改来,使其根据对管理后台的设计来决定加载什么样的缓存策略,如下:

 

ExpandedBlockStart.gif View Code
 1  ///   <summary>
 2  ///  Discuz!NT缓存类
 3  ///  对Discuz!NT论坛缓存进行全局控制管理
 4  ///   </summary>
 5  public  class DNTCache
 6 {
 7     
 8      // 通过该变量决定是否启用MemCached
 9       private  static  bool applyMemCached = MemCachedConfigs.GetConfig().ApplyMemCached;    
10 
11      ///   <summary>
12       ///  构造函数
13       ///   </summary>
14       private DNTCache()
15     {
16          if (applyMemCached)
17             cs =  new MemCachedStrategy();
18          else
19         {
20             cs =  new DefaultCacheStrategy();
21 
22             objectXmlMap = rootXml.CreateElement( " Cache ");
23              // 建立内部XML文档.
24              rootXml.AppendChild(objectXmlMap);
25 
26              // LogVisitor clv = new CacheLogVisitor();
27               // cs.Accept(clv);
28 
29             cacheConfigTimer.AutoReset =  true;
30             cacheConfigTimer.Enabled =  true;
31             cacheConfigTimer.Elapsed +=  new System.Timers.ElapsedEventHandler(Timer_Elapsed);
32             cacheConfigTimer.Start();
33      }
34     }

 

     到这里,主要的开发和修改基本上就告一段落了。下面开始介绍一下如果使用Stats命令来查看缓存的分配和使用等情况。之前在枚举类型Stats中看到该命令有几个主要的参数,分别是:

    stats
    stats reset
    stats malloc
    stats maps   
    stats sizes
    stats slabs
    stats items
    stats cachedump slab_id
     limit_num
    stats detail [on|off|dump]   
    而JAVAEYE的 robbin
    写过一篇文章:贴一段遍历memcached缓存对象的小脚本,来介绍如何使用其中的   “stats cachedump”来获取信息。受这篇文章的启发,我将MemCachedClient.cs文件中的Stats方法加以修改,添加了一个command参数(字符串型),这样就可以向缓存服务器发送上面所说的那几种类型的命令了。

 

 测试代码如下:

 

 

protected void Submit_Click(object sender, EventArgs e)
{
    ArrayList arrayList = new ArrayList();
    arrayList.Add("10.0.1.52:11211");//缓存服务器的地址


 

    StateResult.DataSource = MemCachedManager.GetStats(arrayList, (MemCachedManager.Stats)         
                                     Utils.StrToInt(StatsParam.SelectedValue, 0), Param.Text);
    StateResult.DataBind();           
}
    
     页面代码如下:     
        
我这样做的目的有两个,一个是避免每次都使用telnet协议远程登陆缓存服务器并输入相应的命令行参数(我记忆力不好,参数多了之后就爱忘)。二是将来会把这个页面功能内置到管理后台上,以便后台管理员可以动态监测每台缓存服务器上的数据。

    
好了,到这里今天的内容就差不多了。在本文中我们看到了使用设计模式的好处,通过它我们可以让自己写的代码支持“变化”。这里不妨再多说几句,大家看到了velocity在使用上也是很方便,如果可以的话,未来可以也会将velocity做成一个“缓存策略”,这样站长或管理员就可以根据自己公司的实际情
况来加以灵活配置了。    

    
      相关资料:   
      memcached 全面剖析.pdf   
      memcached 深度分析    

      Facebook 对memcached的提升

转载于:https://www.cnblogs.com/sishierfei/archive/2012/07/13/2590527.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值