memcached单点故障与负载均衡
在上文中,主要教大家如何搭建在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%,看上去数据分布还还很均衡,读取的时候,分别访问从三台服务器内存,再组成完整的数据。这样的数据分发架构,倒真正做到了“负载均衡”。降低了三台服务器的内存使用率,让三台服务器同时为客户端提供服务,这难道不是完美的负载均衡吗?如果没有配置监视工具,也可以参照下面的代码:
- public void testMemcachedProviders()
- {
- int runs = 100;
- int start = 200;
- string keyBase = "testKey";
- string obj = "This is a test of an object blah blah es, serialization does not seem to slow things down so much. The gzip compression is horrible horrible performance, so we only use it for very large objects. I have not done any heavy benchmarking recently";
- //Response.Write(obj);
- //循环记时往服务器缓存上插入数据 等会我们要观察一下数据都存到哪个服务器上的Memcached server上了
- long begin = DateTime.Now.Ticks;
- for (int i = start; i < start + runs; i++)
- {
- // DistCache.Add(keyBase + i, obj);
- }
- long end = DateTime.Now.Ticks;
- long time = end - begin;
- //计算存储这些数据花了多长时间
- //Response.Write(runs + " sets: " + new TimeSpan(time).ToString() + "ms"+"<br/>");
- //开始取数据,并记时
- begin = DateTime.Now.Ticks;
- int hits = 0;
- int misses = 0;
- for (int i = start; i < start + runs; i++)
- {
- string str = (string)DistCache.Get(keyBase + i);
- if (str != null)
- ++hits; //成功取到数据
- else
- ++misses; //丢失次数
- }
- end = DateTime.Now.Ticks;
- time = end - begin;
- //获取这些数据花了多长时间
- Response.Write(runs + " gets: " + new TimeSpan(time).ToString() + "ms"+"<br/>");
- Response.Write("Cache hits: " + hits.ToString() + "<br/>");
- Response.Write("Cache misses: " + misses.ToString() + "<br/>");
- Response.Write("--------------------------------------------------------\r\n");
- }
使用上面的测试代码,可以打印输出处理时间,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。我始终觉得,最原始的,往往就是最灵活的。
- public void testClientLib()
- {
- string[] servers = { "127.0.0.1:11211", "127.0.0.1:11212","127.0.0.1:11213" };//多台服务器构成集群,端口号就是memcached.ini中的listener_port=11212
- string[] serversOne = { "127.0.0.1:11211" };//测试服务器列表
- //初始化池
- SockIOPool pool = SockIOPool.GetInstance();
- // pool.SetServers(servers);
- pool.SetServers(serversOne);//测试服务器
- 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();
- //初始化客户端
- Memcached.ClientLibrary.MemcachedClient mc = new Memcached.ClientLibrary.MemcachedClient();
- mc.EnableCompression = false;
- string keybase = "test";
- //if (mc.Get(keybase) == null)
- //{
- //尝试添加数据
- #region 单个key的情况,value值增大,数据不会自动分布,全都集中在一台服务器上
- //List<int> list = new List<int>();
- //for (int i = 0; i < 100; i++)
- //{
- // list.Add(i);
- //}
- //bool reslut = mc.Add("test", list);
- //if (reslut)
- //{
- // Response.Write("Add cache success");
- //}
- #endregion
- #region 多个key的情况,数据会自动均衡的分布 三台服务器 33%,33%,34%
- //for (int i = 0; i < 100; i++)
- //{
- // bool result = mc.Add(keybase + i, i);
- // if (!result) {
- // Response.Write("Add cache faild");
- // }
- //}
- #endregion
- // }
- // else {
- //object value = mc.Get("test");
- int count = 0;
- for (int i = 0; i < 100; i++)
- {
- object value = mc.Get(keybase + i);
- if(value!=null)
- {
- ++count;
- }
- }
- Response.Write("服务器存储数据量:"+count);
- // }
- pool.Shutdown();
- }
通过本地备份的方式,解决单点故障:
- 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;
- }
- }
- }
memcached单点故障与负载均衡的更多相关文章
- libevent带负载均衡的多线程使用示例
功能: 主线程根据负载工作线程负载均衡算法,每隔一秒钟向特定的工作线程发送一条字符串信息,工作线程简单的把字符串信息打开出来. Makefile eventtest : eventtest.c ...
- 干货 | 亿级Web系统负载均衡几种实现方式
一个执着于技术的公众号 负载均衡(Load Balance)是集群技术(Cluster)的一种应用技术.负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力.目前最常见的负载均衡应用是Web ...
- 终于等到你---订餐系统之负载均衡(nginx+memcached+ftp上传图片+iis)
又见毕业 对面工商大学的毕业生叕在拍毕业照了,一个个脸上都挂满了笑容,也许是满意自己四年的修行,也许是期待步入繁华的社会... 恰逢其时的连绵细雨与满天柳絮,似乎也是在映衬他们心中那些离别的忧伤,与对 ...
- Nginx+Tomcat+Memcached负载均衡集群服务搭建
操作系统:CentOS6.5 本文档主要讲解,如何在CentOS6.5下搭建Nginx+Tomcat+Memcached负载均衡集群服务器,Nginx负责负载均衡,Tomcat负责实际服务,Memc ...
- nginx+tomcat+memcached搭建服务器集群及负载均衡
在实际项目中,由于用户的访问量很大的原因,往往需要同时开启多个服务器才能满足实际需求.但是同时开启多个服务又该怎么管理他们呢?怎样实现session共享呢?下面就来讲一讲如何使用tomcat+ngin ...
- windows使用nginx+memcached实现负载均衡和session或者缓存共享
windows使用nginx+memcached实现负载均衡和session或者缓存共享 两台server server1:115.29.186.215 windows2008 64位操作系统 ser ...
- Nginx + Memcached 实现Session共享的负载均衡
session共享 我们在做站点的试试,通常需要保存用户的一些基本信息,比如登录就会用到Session:当使用Nginx做负载均衡的时候,用户浏览站点的时候会被分配到不同的服务器上,此时如果登录后Se ...
- Tomcat+nginx+keepalived+memcached实现双VIP负载均衡及Session会话保持
准备好tomcat 第一台 tar vxf apache-tomcat-7.0.54.tar.gz mv apache-tomcat-7.0.54 /usr/local/tomcat tar vxf ...
- Nginx+Tomcat8+Memcached实现负载均衡及session共享
1> 基础环境 简易拓扑图: 2> 部署Tomcat [root@node01 ~]# ll -h ~ |egrep 'jdk|tomcat'-rw-r--r-- 1 root root ...
随机推荐
- Linux下vsftp服务器—上传、下载
一. FTP 说明 Linux下常用的FTP Server是vsftp(Very Security File Transfer Protocol),及profpt(Professtional ftp ...
- 三、mysql运算符
取模有2种方法: 或 mod(,) 比较一个字段的值是不是null有两种方法:is null 或 <=> null 不能直接使用 where id = null;是不对的
- multimap和multiset 认知和使用
之前只是在C++ Primer里面看过关联容器,可能因为没有实际用过,只是看看,所以导致用的时候并不熟悉: 在这之前,map和set的特性应该要了解,map是关联数组,也就是由键值对组成的,而set只 ...
- linux whereis which
whereis 命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s). [root@localhost ~]# whereis svn svn ...
- Telerik_2012_Q3 (已破解)全套下载链接
1.Telerik_OpenAccess_ORM_2012_3_1012_SDK.zip (暂未提供下载) 2. Telerik_OpenAccess_ORM_2012_3_1012.zip 3. T ...
- poj 1094 Sorting It All Out (拓扑排序)
http://poj.org/problem?id=1094 Sorting It All Out Time Limit: 1000MS Memory Limit: 10000K Total Su ...
- 解决mysql中表字符集gbk,列字符集Latin1,python查询乱码问题
最近在公司碰到一个异常蛋疼的情况,mysql数据库中,数据库和表的字符集都是'gbk',但是列的字符集却是'latin1',于是蛋疼的事情出现了. 无论我连接字符串的`charset`设置为`gbk` ...
- Ext4.2 grid 条件查询使用
项目中用到Ext4.2,初次接触,用的不是太熟,做个总结,恳请指正! 1.grid重新设置条件,查询结果不是从第1页开始 在处理grid条件查询时,点击搜索按钮调用store.load()方法时,会把 ...
- FileFilter
FileFilter 下面的例子中我们创建了一个FileFilter类,此类根据文件名的扩展名是否为.png来筛选文件.创建FileFilter实例之后需要将此实例作为参数传给File的listFil ...
- Javascript和ECMAScript二三事
来自<javascript高级程序设计 第三版:作者Nicholas C. Zakas>的学习笔记(一) Javascript是一种专为与网页交互而设计的脚本语言,由下列三个不同部分组成: ...