• 【转】系统缓存全解析二:动态缓存(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):

    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定义如下: 

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

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

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

    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的提升

  • 相关阅读:
    网页自动跳转/定时跳转代码
    阿里云ECS用Xshell安装wdcp教程
    wdcp环境下更新PHP到5.3教程
    WDCP 安装教程,超简单!!
    Jquery select 三级联动 (需要JSON数据)
    Jquery实现循环删除Reaper某一行
    jquery更改Reaper某一列的值
    联系电话正则表达式(jquery表单验证)
    Jquery获取选中的checkbox的值
    sql语句创建主键、外键、索引、绑定默认值
  • 原文地址:https://www.cnblogs.com/sishierfei/p/2590527.html
Copyright © 2020-2023  润新知