在上文中, 主要教大家如何搭建在windows IIS 7.5下搭建php环境,使用常见的两种memcached性能监视工具。通过自己动手实践,观察监控工具上数据,相信大家对于memcached的了解 一定深入了很多。但是同样还有些疑惑。本文将用图文的方式,继续讲解memcached在集群环境下的使用技巧。
曾经看到过这样的文字(大概是翻译过来的,算是比较权威的)
memcached如何处理容错的?
不处理!:) 在memcached节点失效的情况下,集群没有必要做任何容错处理。如果发生了节点失效,应对的措施完全取决于用户。节点失效时,下面列出几种方案供您选择:
* 忽略它! 在失效节点被恢复或替换之前,还有很多其他节点可以应对节点失效带来的影响。
* 把失效的节点从节点列表中移除。做这个操作千万要小心!在默认情况下(余数式哈希算法),客户端添加或移除节点,会导致所有的缓存数据不可用!因为哈希参照的节点列表变化了,大部分key会因为哈希值的改变而被映射到(与原来)不同的节点上
* 启动热备节点,接管失效节点所占用的IP。这样可以防止哈希紊乱(hashing chaos)。
根据上面的说法,memcached其中一个节点失效以后,memcached本身是没有任何策略维持失效转发的,这对于大型系统是一个无法接受的事实。
Memcached基于一 个存储键/值对的hashmap。其守护进程是用C写的,但是客户端可以用任何语言来编写(本文使用C#作为例子),并通过memcached协议与守护进程通信。可 能这些东西都太高深了,我们暂不做研究。
虽然Memcached作为一个分布式缓存数据服务,但是每个服务之间根本没有进行相互通信,这里可能与 我理解的分布式有点区别,可能是我才疏学浅,也可能是每个人思考问题的角度不同。Memcached 客户端就是通过一种分布式算法将数据保存到不同的Memcached服务器上,将数据进行缓存。
Memcached分布式环境下,每个服务器端本身没有相互相连的关系,数据分布其实是由客户端来维持的(通俗点说,是客户端按照自己的分布算法,将数据分配 给指定的服务端去存储,取值的时候,客户端再找指定的服务器拿数据。任何环境下,服务端都不可能主动去找客户端拿“东西”或者去操作客户端。B/S模式也 是的,web服务器不可能主动找浏览器拿东西,更不可能对浏览器端做任何操作)。memcached的服务端更不会这么聪明,自动去查找、匹配当前环境 中分布的其他服务器。
而且,据我所知,Memcached本身并没有为集群提供真的高可用方案,因为我个人认为,使用集群环境,通常是为了满足以下的需求:
1.压力分载 (负载均衡) 2.失效转发(故障转移)。
而memcached本身并不具备这两点,这对于以“分布式缓存”号称的memcached来说,是非常致命的。对于笔者来说,也是一种沉痛的打击啊(o(∩_∩)o 哈哈)。
理论上来讲,客户端连接多个memcached服务端的时候,默认的数据分布是这样的:
理论上的,%33+33%+34%=100%,看上去数据分布还还很均衡,读取的时候,分别访问从三台服务器内存,再组成完整的数据。这样的数据分 发架构,倒真正做到了“负载均衡”。降低了三台服务器的内存使用率,让三台服务器同时为客户端提供服务,这难道不是完美的负载均衡吗?如果没有配置监视工 具,也可以参照下面的代码:
使用上面的测试代码,可以打印输出处理时间,get/set次数。分别注释掉配置文件中指定memcached服务器配置后,再读取测试,可以清楚的看到数据分布比例。
我本地开启了3个memcached服务,分别指向不同端口,数据的分布比例是这样的: 37%,43%,20%。没有理论上的那么均衡。
有过分布式集群架构的朋友,肯定会想到,那万一发生了“单点故障”(就像sqlserver集群中的,单个节点上的数据库服务器宕机),那不是玩完了?
按照上图所示,一台服务器宕机了,就有33%的数据丢失了。那不就玩完了。如果是某银行采用这种架构,发生如此杯具,那架构师岂不是要被群众拿刀砍死。
那到底该如何解决这个问题呢?我翻阅了很多中文甚至英文的资料,好像真的没有官方或者很权威的解决方案。提供了如下两种思路。
解决方案1:本地备份缓存
在本地放一份缓存,同时也在分布式Memcached上放一份缓存,如果当其中一台节点当机了,客户端程序直接读取本地的缓存,本地客户端维护一个 HashMap即可,这样的方案虽然很简陋,但是可以满足一部分场景的需要,当你很急需的时候可以作为临时方案暂时替代一下。
解决方案2:采用缓存代理服务器
采用 Magent 缓存代理,防止单点现象,缓存代理也可以做备份,通过客户端连接到缓存代理服务器,缓存代理服务器连接缓存服务器,缓存代理服务器可以连接多台Memcached机器可以将每台Memcached机器进行数据同步。这样的架构比较完善了,如果其中一台缓存代理服务器down机,系统依然可以继续工作,如果其中一台Memcached机器down掉,数据不会丢失并且可以保证数据的完整性,以上描述的系统架构如图所示:
在笔者的实践中,沿袭了第一种方案的思想。由于笔者项目使用的是windows的服务器,而第二种方案中的magent代理软件,好像只支持linux平台。
在客户端还是配置多台服务器,但是让其中任意的一台服务器做备份,去读取并append另外几台服务器的数据,这样依赖,该台备份服务器上就始终存 储了一份完整的数据。当发生意外情况的时候,直接读取备份服务器上的数据。等服务器故障恢复后,再从客户端,将数据合理的分发出去。
在.NET平台下,就不能选用enyim.com Memcached Client或者Memcached Providers之类封装得太完善的client啦!涉及到很多基本的操作,这里推荐使用.NET memcached client library这个比较原始的类库client。我始终觉得,最原始的,往往就是最灵活的。
通过本地备份的方式,解决单点故障:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Web;
using Memcached.ClientLibrary;
namespace MemcachedPro
{
public class MemcacheProvider
{
MemcachedClient mainClient;
MemcachedClient backupClient;
/// <summary>
/// 在构造函数中,初始化客户端(主/备)
/// </summary>
public MemcacheProvider()
{
//主服务器客户端
mainClient = new MemcachedClient();
mainClient.PoolName = GetMainPollName();
mainClient.EnableCompression = false;
//备份服务器客户端
backupClient = new MemcachedClient();
backupClient.PoolName = GetBackUpPollName();
backupClient.EnableCompression = false;
}
/// <summary>
/// 初始化主服务器pool
/// </summary>
/// <returns></returns>
public string GetMainPollName()
{
//string[] Servers = { "127.0.0.1:11211" };//测试服务器列表
string strServers = ConfigurationManager.AppSettings["memcacheMainServer"];
string[] Servers = strServers.Split(';');
//初始化池
SockIOPool pool = SockIOPool.GetInstance("p1");
pool.SetServers(Servers);//测试服务器
pool.InitConnections = 3;
pool.MinConnections = 3;
pool.MaxConnections = 5;
pool.SocketConnectTimeout = 1000;
pool.SocketTimeout = 3000;
pool.MaintenanceSleep = 30;
pool.Failover = true;
pool.Nagle = false;
pool.Initialize();
return "p1";
}
/// <summary>
/// 初始化备份服务器pool
/// </summary>
/// <returns></returns>
public string GetBackUpPollName()
{
// string[] Servers = { "127.0.0.1:11212" };//备份服务器列表
string strServers = ConfigurationManager.AppSettings["memcacheBackupServer"];
string[] Servers = strServers.Split(';');
//初始化池
SockIOPool pool = SockIOPool.GetInstance("p2");
pool.SetServers(Servers);//测试服务器
pool.InitConnections = 3;
pool.MinConnections = 3;
pool.MaxConnections = 5;
pool.SocketConnectTimeout = 1000;
pool.SocketTimeout = 3000;
pool.MaintenanceSleep = 30;
pool.Failover = true;
pool.Nagle = false;
pool.Initialize();
return "p2";
}
/// <summary>
/// 设置值
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool SetCache(string key, object value)
{
bool result = false;
try
{
//设置到主服务器组
result = mainClient.Set(key, value);
//设置备份
result = backupClient.Set(key, value);
}
catch (Exception)
{
//发送短信或者邮件提醒
throw;
}
return result;
}
/// <summary>
/// 取值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object GetCache(string key)
{
object value = null;
//先读主服务器
try
{
value = mainClient.Get(key);
//如果没取到值
if (value == null)
{
//发送短信或者邮件提醒:可能主服务器宕机了
//从备份服务器取值
value = backupClient.Get(key);
if (value == null)
{
//从备份服务器取值也失败,发送短信或者邮件提醒
}
}
}
catch (Exception)
{
//发送短信或者邮件提醒
throw;
}
return value;
}
/// <summary>
/// 当主服务器恢复运行后(数据已经丢失了),将备份服务器中的缓存同步到主服务器
/// </summary>
/// <returns></returns>
public bool RestoreCache()
{
bool result = false;
return result;
}
}
}
好了,由于篇幅有限。本文就到此了。 本文出自/blog.csdn.net/dinglang_2009 ,转载请注明出处