天堂1111 发表于 2015-8-31 12:08:17

遍历memcached缓存对象(C#)

  STATS命令

  出于性能考虑,memcached没有提供遍历功能,不过我们可以通过以下两个stats命令得到所有的缓存对象。
  1、stats items

  显示各个slab中item的数目。
  2、stats cachedump slab_id limit_num
   显示某个slab中的前limit_num个key列表,显示格式:ITEM key_name [ value_length b; expire_time|access_time s]
  除了上面两个,memcached还提供了以下命令:

  3、stats
4、 stats reset
5、 stats malloc
6、 stats maps
7、 stats sizes
8、 stats slabs
9、 stats detail
  命令的用法就不一一说了,请自行google。 关于memcached的数据存储和内存分配以后有机会再写。

  添加缓存
  在本地添加几个key,如下:

  程序实现
  因为要用c#调用,所以需要客户端执行 STATS 命令,这个可以直接参考DiscuzNT3.0中的实现。
  DiscuzNT下载地址:http://download.comsenz.com/DiscuzNT/src/

  下载完程序以后,在Discuz.Cache项目中找到这两个类:MemCached.cs和MemCachedClient.cs。
  我们要用到的方法有:
  MemCached.GetStats

代码

      /// <summary>
      /// 获取服务器端缓存的数据信息
      /// </summary>
      /// <param name="serverArrayList">要访问的服务列表</param>
      /// <returns>返回信息</returns>
      public static ArrayList GetStats(ArrayList serverArrayList, Stats statsCommand, string param)
      {
            ArrayList statsArray = new ArrayList();
            param =Utils.StrIsNullOrEmpty(param) ? "" : param.Trim().ToLower();
            string commandstr = "stats";
            //转换stats命令参数
            switch (statsCommand)
            {
                case Stats.Reset: { commandstr = "stats reset"; break; }
                case Stats.Malloc: { commandstr = "stats malloc"; break; }
                case Stats.Maps: { commandstr = "stats maps"; break; }
                case Stats.Sizes: { commandstr = "stats sizes"; break; }
                case Stats.Slabs: { commandstr = "stats slabs"; break; }
                case Stats.Items: { commandstr = "stats"; break; }
                case Stats.CachedDump:
                {
                  string[] statsparams = Utils.SplitString(param, " ");
                  if(statsparams.Length == 2)
                        if(Utils.IsNumericArray(statsparams))
                            commandstr = "stats cachedump" + param;
                  break;                     
                }
                case Stats.Detail:
                  {
                        if(string.Equals(param, "on") || string.Equals(param, "off") || string.Equals(param, "dump"))
                            commandstr = "stats detail " + param.Trim();
                        break;
                  }
                default: { commandstr = "stats"; break; }
            }
            //加载返回值
            Hashtable stats = MemCachedManager.CacheClient.Stats(serverArrayList, commandstr);
            foreach (string key in stats.Keys)
            {
                statsArray.Add(key);
                Hashtable values = (Hashtable)stats;
                foreach (string key2 in values.Keys)
                {
                  statsArray.Add(key2 + ":" + values);
                }
            }
            return statsArray;
      }  MemCachedClient.Stats


代码

public Hashtable Stats(ArrayList servers, string command)
      {
            // get SockIOPool instance
            SockIOPool pool = SockIOPool.GetInstance(_poolName);
            // return false if unable to get SockIO obj
            if(pool == null)
            {
                //if(log.IsErrorEnabled)
                //{
                //    log.Error(GetLocalizedString("unable to get socket pool"));
                //}
                return null;
            }
            // get all servers and iterate over them
            if (servers == null)
                servers = pool.Servers;
            // if no servers, then return early
            if(servers == null || servers.Count <= 0)
            {
                //if(log.IsErrorEnabled)
                //{
                //    log.Error(GetLocalizedString("stats no servers"));
                //}
                return null;
            }
            // array of stats Hashtables
            Hashtable statsMaps = new Hashtable();
            for(int i = 0; i < servers.Count; i++)
            {
                SockIO sock = pool.GetConnection((string)servers);
                if(sock == null)
                {
                  //if(log.IsErrorEnabled)
                  //{
                  //    log.Error(GetLocalizedString("unable to connect").Replace("$$Server$$", servers.ToString()));
                  //}
                  continue;
                }
                // build command
                command = Discuz.Common.Utils.StrIsNullOrEmpty(command) ?"stats\r\n": command + "\r\n";
                try
                {
                  sock.Write(UTF8Encoding.UTF8.GetBytes(command));
                  sock.Flush();
                  // map to hold key value pairs
                  Hashtable stats = new Hashtable();
                  // loop over results
                  while(true)
                  {
                        string line = sock.ReadLine();
                        //if(log.IsDebugEnabled)
                        //{
                        //    log.Debug(GetLocalizedString("stats line").Replace("$$Line$$", line));
                        //}

                        if(line.StartsWith(STATS))
                        {
                            string[] info = line.Split(' ');
                            string key    = info;
                            string val= info;
                            //if(log.IsDebugEnabled)
                            //{
                            //    log.Debug(GetLocalizedString("stats success").Replace("$$Key$$", key).Replace("$$Value$$", val));
                            //}

                            stats[ key ] = val;
                        }
                        else if(END == line)
                        {
                            // finish when we get end from server
                            //if(log.IsDebugEnabled)
                            //{
                            //    log.Debug(GetLocalizedString("stats finished"));
                            //}
                            break;
                        }
                        statsMaps[ servers ] = stats;
                  }
                }
                catch//(IOException e)
                {
                  //if(log.IsErrorEnabled)
                  //{
                  //    log.Error(GetLocalizedString("stats IOException"), e);
                  //}

                  try
                  {
                        sock.TrueClose();
                  }
                  catch//(IOException)
                  {
                        //if(log.IsErrorEnabled)
                        //{
                        //    log.Error(GetLocalizedString("failed to close some socket").Replace("$$Socket$$", sock.ToString()));
                        //}
                  }
                  sock = null;
                }
                if(sock != null)
                  sock.Close();
            }
            return statsMaps;
      }  有了这两个方法我们就可以得到memcached中的缓存项了。

  基本思路是,先得到cache中所有的item(stats items),再通过itemid 取出cachekey和cachevalue(stats cachedump)

  程序实现如下:


    privatevoid GetItems()
    {
      ArrayList itemarr = new ArrayList();
      ArrayList arrayList = new ArrayList();
      StringBuilder sb = new StringBuilder();
      foreach (string server in MemCachedManager.ServerList)
      {
            arrayList.Add(server);
      }
      ArrayList arr = MemCachedManager.GetStats(arrayList, MemCachedManager.Stats.Items, null);
      foreach (string a in arr)
      {
            string[] tmparr = a.Split(':');
            if (tmparr.Length > 1)
            {
                int item_id = 0;
                int.TryParse(tmparr, out item_id);
                bool find = false;
                foreach (int item in itemarr)
                {
                  if (item == item_id)
                        find = true;
                }
                if (!find && item_id > 0 && item_id != 11211)
                  itemarr.Add(item_id);
            }
      }
      foreach (int item in itemarr)
      {
            sb.Append("item " + item + "<br />");
            ArrayList cachearr = MemCachedManager.GetStats(arrayList, MemCachedManager.Stats.CachedDump, "" + item + " 10");
            foreach (string cache in cachearr)
            {
                sb.Append(cache);
                sb.Append("<br />");
            }
      }
      Response.Write(sb.ToString());
    }  运行程序:



  为什么没有输出缓存项呢?
  DiscuzNT3.0中的bug
  于是我找啊找,发现是DiscuzNT3.0中的一个bug导致。
  在MemCachedClient.Stats中,有这样的一段代码:


if(line.StartsWith(STATS))
{
    string[] info = line.Split(' ');
    string key    = info;
    string val= info;
    stats[ key ] = val;
}
else if(END == line)
{
    break;
}  原来是忽略了stats cachedump 的结果是以ITEM开头的,所以什么都没有输出。简单修改一下:


if(line.StartsWith(STATS) )
{
    string[] info = line.Split(' ');
    string key    = info;
    string val= info;
    stats[ key ] = val;
}
else if (line.StartsWith("ITEM"))
{
    string[] info = line.Split('[');
    string key = info.Split(' ');
    string val = "[" + info;
    stats = val;
}
else if (END == line)
{
    break;
}  再看一下输出结果,显示正常。



  
  
  
  
页: [1]
查看完整版本: 遍历memcached缓存对象(C#)