Memcached分布式,集群式环境下,如何解决单点故障,以及memcached负载均衡的解决方案.
之前曾经写过Memcached本身没有实现分布式,服务端之间根本不相互通信,所以在单点故障发生时,也没有任何容错机制去维持失效的节点.
下面的图例很好的诠释了单点故障的情况:
所以在单点故障发生后,也就是其中的部分数据会丢失,那无论是对系统本身,或者是使用者来说,这都是不能被接受的.
解决的方法基本有两个种:
解决方案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掉,数据不会丢失并且可以保证数据的完整性,以上描述的系统架构如图所示:
因为Magent只能在Linux下使用,所以无法实践了,可以看下这篇文章:http://zhumeng8337797.blog.163.com/blog/static/10076891420113431424757/



浙公网安备 33010602011771号