设为首页 收藏本站
查看: 1246|回复: 0

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

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-9-2 11:27:44 | 显示全部楼层 |阅读模式
  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):

DSC0000.gif DSC0001.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 { set; get; }
71 }  
  上面类实现的接口Discuz.Cache.ICacheStrategy定义如下:

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一些常操作和相关初始化实例调用的&#8220;封装&#8221;,下面是是其变量定义和初始化构造方法的代码:

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命令来获取当前缓存服务器上的数据信息和内存分配信息等,相应的方法如下(详情见注释):
  

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):
  

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     /// 链接失败后是否重启,详情参见http://baike.baidu.com/view/1084309.htm
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的主要性能都是通过这些参数来决定的,大家应根据自己公司产品和应用的实际情况配置相应的数值。

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

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缓存对象的小脚本,来介绍如何使用其中的   &#8220;stats cachedump&#8221;来获取信息。受这篇文章的启发,我将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协议远程登陆缓存服务器并输入相应的命令行参数(我记忆力不好,参数多了之后就爱忘)。二是将来会把这个页面功能内置到管理后台上,以便后台管理员可以动态监测每台缓存服务器上的数据。
  
好了,到这里今天的内容就差不多了。在本文中我们看到了使用设计模式的好处,通过它我们可以让自己写的代码支持&#8220;变化&#8221;。这里不妨再多说几句,大家看到了velocity在使用上也是很方便,如果可以的话,未来可以也会将velocity做成一个&#8220;缓存策略&#8221;,这样站长或管理员就可以根据自己公司的实际情
况来加以灵活配置了。   
   
      相关资料:   
      memcached 全面剖析.pdf   
      memcached 深度分析   
  Facebook 对memcached的提升

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-108672-1-1.html 上篇帖子: [centos 6.3]php客户端memcached扩展安装 下篇帖子: memcached命令行用法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表