Memcached分布式,集群式环境下,如何解决单点故障,以及memcached负载均衡的解决方案.

之前曾经写过Memcached本身没有实现分布式,服务端之间根本不相互通信,所以在单点故障发生时,也没有任何容错机制去维持失效的节点.

下面的图例很好的诠释了单点故障的情况:

masd-1_s

 

masd-2_s

 

所以在单点故障发生后,也就是其中的部分数据会丢失,那无论是对系统本身,或者是使用者来说,这都是不能被接受的.

解决的方法基本有两个种:

解决方案1:使用备份服务器的方法.一组是主服务器组,一组是备份服务器组.初始化的时候分别初始化主服务器组和备份服务器组,设置值的时候也要分别对主服务器和备份服务器进行Set.

而在Get的时候都是从主服务器端取,当取不到值的时候(假定连续取三次,每次有一定的时间间隔),我们就从备份服务器取值.当主服务器恢复运行后(数据已经丢失了)将备份服务器中的缓存同步到主服务器.

这种方法的Code网上可以找到,关键是缓存数据如何同步呢.

基本思路:先得到cache中所有的item(命令:stats slabs,stats items),再通过itemid取出cache的key和value(命令:stats cachedump)

然后遍历所有的cache,同时往主服务器组去更新所有的cache.

下面的获取遍历所有cache的代码:

      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)
         {
            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)
         {
            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)
            {
               continue;
            }

            //build command
            command = string.IsNullOrEmpty(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 (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('[');
                     stringkey = info[0].Split(' ')[1];
                     stringval = "[" + info[1];

                     stats[key] = val;
                  }
                  else if ("END" == line)
                  {
                     break;
                  }

                  statsMaps[servers[i]] = stats;
               }
            }
            catch
            {
               try
               {
                  sock.TrueClose();
               }
               catch { }              

               sock = null;
            }

            if (sock != null)
               sock.Close();
         }

         return statsMaps;
     }

      /// <summary>
      /// 获取服务器端缓存的数据信息
      /// </summary>
      /// <param name="serverArrayList">要访问的服务列表</param>
      /// <param name="statsCommand"></param>
      /// <param name="param"></param>
      /// <returns></returns>

      public ArrayList GetStats(ArrayList serverArrayList, Statss statsCommand, string param)
      {
         ArrayList statsArray = new ArrayList();
         param = string.IsNullOrEmpty(param) ? "" : param.Trim().ToLower();
         string commandstr = "stats";
         //转换stats命令参数
         switch (statsCommand)
         {
            case Statss.Reset: { commandstr = "stats reset"; break; }
            case Statss.Malloc: { commandstr = "stats malloc"; break; }
            case Statss.Maps: { commandstr = "stats maps"; break; }
            case Statss.Sizes: { commandstr = "stats sizes"; break; }
            case Statss.Slabs: { commandstr = "stats slabs"; break; }
            case Statss.Items: { commandstr = "stats"; break; }
            case Statss.CachedDump:
               {
                  string[] statsparams = Regex.Split(param, Regex.Escape(" "), RegexOptions.IgnoreCase);
                  if (statsparams.Length == 2)
                     if (IsNumericArray(statsparams))
                        commandstr = "stats cachedump  " + param;

                  break;
               }
            case Statss.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 = 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;
      }

      private void GetItems(ArrayList serverArrayList)
      {
         ArrayList itemarr = new ArrayList();

         ArrayList arrayList = new ArrayList();
         StringBuilder sb = new StringBuilder();

         foreach(string server in serverArrayList)
         {
            arrayList.Add(server);
         }
         ArrayList arr = GetStats(arrayList, Statss.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)
         {
            ArrayList cachearr = GetStats(arrayList, Statss.CachedDump, "" + item + " 10");
         }
      }

 

下面把stats常用命令也贴出来:

stats
显示服务器信息、统计数据等
stats reset
清空统计数据
stats malloc
显示内存分配数据
stats cachedump slab_id limit_num
显示某个slab中的前limit_num个key列表,显示格式如下
ITEM key_name [ value_length b; expire_time|access_time s]
其中,memcached 1.2.2及以前版本显示的是  访问时间(timestamp)
1.2.4以上版本,包括1.2.4显示 过期时间(timestamp)
如果是永不过期的key,expire_time会显示为服务器启动的时间

stats cachedump 7 2
ITEM copy_test1 [250 b; 1207795754 s]
ITEM copy_test [248 b; 1207793649 s]
stats slabs
显示各个slab的信息,包括chunk的大小、数目、使用情况等
stats items
显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)
stats detail [on|off|dump]
设置或者显示详细操作记录

参数为on,打开详细操作记录
参数为off,关闭详细操作记录
参数为dump,显示详细操作记录(每一个键值get、set、hit、del的次数)

stats detail dump
PREFIX copy_test2 get 1 hit 1 set 0 del 0
PREFIX copy_test1 get 1 hit 1 set 0 del 0
PREFIX cpy get 1 hit 0 set 0 del 0
 

解决方案2:采用缓存代理服务器
    采用
Magent 缓存代理,防止单点现象,缓存代理也可以做备份,通过客户端连接到缓存代理服务器,缓存代理服务器连接缓存服务器,缓存代理服务器可以连接多台Memcached机器可以将每台Memcached机器进行数据同步。这样的架构比较完善了,如果其中一台缓存代理服务器down机,系统依然可以继续工作,如果其中一台Memcached机器down掉,数据不会丢失并且可以保证数据的完整性,以上描述的系统架构如图所示:

0_13223698887wxx 

因为Magent只能在Linux下使用,所以无法实践了,可以看下这篇文章:http://zhumeng8337797.blog.163.com/blog/static/10076891420113431424757/

 
 
posted on 2013-08-21 09:38  tzj19810812  阅读(280)  评论(0)    收藏  举报