asp.net(C#)遍历memcached缓存对象

  STATS命令

  遍历memcached缓存对象(C#)转载之青草堂

  出于性能考虑,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 [on|off|dump]

  命 令的用法就不一一说了,请自行google。 关于memcached的数据存储和内存分配以后有机会再写。

  添加 缓存

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

asp.net(C#)遍历memcached缓存对象

  程序实现

  因为要用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[key];

  foreach (string key2 in values.Keys)

  {

  statsArray.Add(key2 + ":" + values[key2]);

  }

  }

  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[i]);

  if(sock == null)

  {

  //if(log.IsErrorEnabled)

  //{

  // log.Error(GetLocalizedString("unable to connect").Replace("$$Server$$", servers[i].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[1];

  string val = info[2];

  //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[i] ] = 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)

  程序实现如下:

  

复制代码 代码如下:

  private void 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[1], 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());

  }

  运行程序:

asp.net(C#)遍历memcached缓存对象

  为什么没有输出缓存项呢?

  DiscuzNT3.0中的bug

  于是我找啊找,发现是DiscuzNT3.0中的一个bug导致。

  在MemCachedClient.Stats中,有这样的一段代码:

  

复制代码 代码如下:

  if(line.StartsWith(STATS))

  {

  string[] info = line.Split(' ');

  string key = info[1];

  string val = info[2];

  stats[ key ] = val;

  }

  else if(END == line)

  {

  break;

  }

  原来是忽略了stats cachedump 的结果是以ITEM开头的,所以什么都没有输出。简单修改一下:

  

复制代码 代码如下:

  if(line.StartsWith(STATS) )

  {

  string[] info = line.Split(' ');

  string key = info[1];

  string val = info[2];

  stats[ key ] = val;

  }

  else if (line.StartsWith("ITEM"))

  {

  string[] info = line.Split('[');

  string key = info[0].Split(' ')[1];

  string val = "[" + info[1];

  stats[key] = val;

  }

  else if (END == line)

  {

  break;

  }

  再看一下输出结果,显示正常。

asp.net(C#)遍历memcached缓存对象