`
mowengaobo
  • 浏览: 166237 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

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%,看上去数据分布还还很均衡,读取的时候,分别访问从三台服务器内存,再组成完整的数据。这样的数据分发架构,倒真正做到了“负载均衡”。降低了三台服务器的内存使用率,让三台服务器同时为客户端提供服务,这难道不是完美的负载均衡吗?如果没有配置监视工具,也可以参照下面的代码:

 

Csharp代码 
  1. public void testMemcachedProviders()   
  2.        {  
  3.            int runs = 100;  
  4.            int start = 200;  
  5.             
  6.            string keyBase = "testKey";  
  7.            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";  
  8.              
  9.            //Response.Write(obj);  
  10.            //循环记时往服务器缓存上插入数据  等会我们要观察一下数据都存到哪个服务器上的Memcached server上了  
  11.            long begin = DateTime.Now.Ticks;  
  12.            for (int i = start; i < start + runs; i++)  
  13.            {   
  14.              // DistCache.Add(keyBase + i, obj);  
  15.            }  
  16.            long end = DateTime.Now.Ticks;  
  17.            long time = end - begin;  
  18.   
  19.            //计算存储这些数据花了多长时间  
  20.            //Response.Write(runs + " sets: " + new TimeSpan(time).ToString() + "ms"+"<br/>");  
  21.   
  22.            //开始取数据,并记时  
  23.            begin = DateTime.Now.Ticks;  
  24.            int hits = 0;  
  25.            int misses = 0;  
  26.            for (int i = start; i < start + runs; i++)  
  27.            {  
  28.                string str = (string)DistCache.Get(keyBase + i);  
  29.                if (str != null)  
  30.                    ++hits;    //成功取到数据  
  31.                else  
  32.                    ++misses;  //丢失次数  
  33.            }  
  34.            end = DateTime.Now.Ticks;  
  35.            time = end - begin;  
  36.   
  37.            //获取这些数据花了多长时间  
  38.            Response.Write(runs + " gets: " + new TimeSpan(time).ToString() + "ms"+"<br/>");  
  39.            Response.Write("Cache hits: " + hits.ToString() + "<br/>");  
  40.            Response.Write("Cache misses: " + misses.ToString() + "<br/>");  
  41.            Response.Write("--------------------------------------------------------\r\n");  
  42.              
  43.        }  

使用上面的测试代码,可以打印输出处理时间,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。我始终觉得,最原始的,往往就是最灵活的。

 

Csharp代码 
  1. public void testClientLib()   
  2.        {  
  3.            string[] servers = { "127.0.0.1:11211""127.0.0.1:11212","127.0.0.1:11213" };//多台服务器构成集群,端口号就是memcached.ini中的listener_port=11212  
  4.            string[] serversOne = { "127.0.0.1:11211" };//测试服务器列表  
  5.            //初始化池  
  6.            SockIOPool pool = SockIOPool.GetInstance();  
  7.           // pool.SetServers(servers);  
  8.            pool.SetServers(serversOne);//测试服务器  
  9.            pool.InitConnections = 3;  
  10.            pool.MinConnections = 3;  
  11.            pool.MaxConnections = 5;  
  12.            pool.SocketConnectTimeout = 1000;  
  13.            pool.SocketTimeout = 3000;  
  14.            pool.MaintenanceSleep = 30;  
  15.            pool.Failover = true;  
  16.            pool.Nagle = false;  
  17.            pool.Initialize();  
  18.            //初始化客户端  
  19.            Memcached.ClientLibrary.MemcachedClient mc = new Memcached.ClientLibrary.MemcachedClient();  
  20.            mc.EnableCompression = false;  
  21.   
  22.            string keybase = "test";  
  23.            //if (mc.Get(keybase) == null)  
  24.            //{  
  25.                //尝试添加数据  
  26.                #region 单个key的情况,value值增大,数据不会自动分布,全都集中在一台服务器上  
  27.                //List<int> list = new List<int>();  
  28.                //for (int i = 0; i < 100; i++)  
  29.                //{  
  30.                //    list.Add(i);  
  31.                //}  
  32.                //bool reslut =  mc.Add("test", list);  
  33.                //if (reslut)  
  34.                //{  
  35.                //    Response.Write("Add cache success");  
  36.                //}  
  37.                 
  38.                #endregion  
  39.                #region 多个key的情况,数据会自动均衡的分布  三台服务器 33%,33%,34%  
  40.                //for (int i = 0; i < 100; i++)  
  41.                //{  
  42.                //    bool result = mc.Add(keybase + i, i);  
  43.                //    if (!result) {  
  44.                //        Response.Write("Add cache faild");  
  45.                //    }  
  46.                //}  
  47.                #endregion  
  48.                  
  49.           // }  
  50.           // else {  
  51.                //object value = mc.Get("test");  
  52.                int count = 0;  
  53.                for (int i = 0; i < 100; i++)  
  54.                {  
  55.                    object value = mc.Get(keybase + i);  
  56.                    if(value!=null)  
  57.                    {  
  58.                        ++count;  
  59.                    }  
  60.                }  
  61.                Response.Write("服务器存储数据量:"+count);  
  62.         //   }  
  63.              
  64.            pool.Shutdown();  
  65.        }  

通过本地备份的方式,解决单点故障:

 

Csharp代码 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Configuration;  
  6. using System.Web;  
  7. using Memcached.ClientLibrary;  
  8.   
  9.   
  10. namespace MemcachedPro  
  11. {  
  12.    public  class MemcacheProvider  
  13.     {  
  14.        MemcachedClient mainClient;  
  15.        MemcachedClient backupClient;  
  16.         /// <summary>  
  17.         /// 在构造函数中,初始化客户端(主/备)  
  18.         /// </summary>  
  19.         public MemcacheProvider()   
  20.         {  
  21.             //主服务器客户端  
  22.             mainClient = new MemcachedClient();  
  23.             mainClient.PoolName = GetMainPollName();  
  24.             mainClient.EnableCompression = false;  
  25.              
  26.             //备份服务器客户端  
  27.             backupClient = new MemcachedClient();  
  28.             backupClient.PoolName = GetBackUpPollName();  
  29.             backupClient.EnableCompression = false;  
  30.         }  
  31.       /// <summary>  
  32.       /// 初始化主服务器pool  
  33.       /// </summary>  
  34.       /// <returns></returns>  
  35.        public  string GetMainPollName()  
  36.         {  
  37.             //string[] Servers = { "127.0.0.1:11211" };//测试服务器列表  
  38.             string strServers = ConfigurationManager.AppSettings["memcacheMainServer"];  
  39.             string[] Servers = strServers.Split(';');  
  40.            //初始化池  
  41.             SockIOPool pool = SockIOPool.GetInstance("p1");  
  42.             pool.SetServers(Servers);//测试服务器  
  43.             pool.InitConnections = 3;  
  44.             pool.MinConnections = 3;  
  45.             pool.MaxConnections = 5;  
  46.             pool.SocketConnectTimeout = 1000;  
  47.             pool.SocketTimeout = 3000;  
  48.             pool.MaintenanceSleep = 30;  
  49.             pool.Failover = true;  
  50.             pool.Nagle = false;  
  51.             pool.Initialize();  
  52.             return "p1";  
  53.         }  
  54.        /// <summary>  
  55.        /// 初始化备份服务器pool  
  56.        /// </summary>  
  57.        /// <returns></returns>  
  58.         public string GetBackUpPollName()  
  59.         {  
  60.            // string[] Servers = { "127.0.0.1:11212" };//备份服务器列表  
  61.             string strServers = ConfigurationManager.AppSettings["memcacheBackupServer"];  
  62.             string[] Servers = strServers.Split(';');  
  63.             //初始化池  
  64.             SockIOPool pool = SockIOPool.GetInstance("p2");  
  65.             pool.SetServers(Servers);//测试服务器  
  66.             pool.InitConnections = 3;  
  67.             pool.MinConnections = 3;  
  68.             pool.MaxConnections = 5;  
  69.             pool.SocketConnectTimeout = 1000;  
  70.             pool.SocketTimeout = 3000;  
  71.             pool.MaintenanceSleep = 30;  
  72.             pool.Failover = true;  
  73.             pool.Nagle = false;  
  74.             pool.Initialize();  
  75.             return "p2";  
  76.         }  
  77.        /// <summary>  
  78.        /// 设置值  
  79.        /// </summary>  
  80.        /// <param name="key"></param>  
  81.        /// <param name="value"></param>  
  82.        /// <returns></returns>  
  83.         public bool SetCache(string key, object value)  
  84.         {  
  85.             bool result = false;  
  86.             try  
  87.             {  
  88.                 //设置到主服务器组  
  89.                 result = mainClient.Set(key, value);  
  90.   
  91.                 //设置备份  
  92.   
  93.                 result = backupClient.Set(key, value);  
  94.             }  
  95.             catch (Exception)  
  96.             {  
  97.                 //发送短信或者邮件提醒  
  98.                 throw;  
  99.             }  
  100.              
  101.             return result;  
  102.         }  
  103.        /// <summary>  
  104.        /// 取值  
  105.        /// </summary>  
  106.        /// <param name="key"></param>  
  107.        /// <returns></returns>  
  108.         public object GetCache(string key)  
  109.         {  
  110.             object value = null;  
  111.             //先读主服务器  
  112.             try  
  113.             {  
  114.                 value = mainClient.Get(key);  
  115.                 //如果没取到值  
  116.                 if (value == null)  
  117.                 {  
  118.                     //发送短信或者邮件提醒:可能主服务器宕机了  
  119.   
  120.                     //从备份服务器取值  
  121.                     value = backupClient.Get(key);  
  122.                     if (value == null)  
  123.                     {  
  124.                         //从备份服务器取值也失败,发送短信或者邮件提醒  
  125.                     }  
  126.                 }  
  127.             }  
  128.             catch (Exception)  
  129.             {  
  130.                 //发送短信或者邮件提醒  
  131.                 throw;  
  132.             }  
  133.               
  134.              
  135.             return value;  
  136.         }  
  137.        /// <summary>  
  138.        /// 当主服务器恢复运行后(数据已经丢失了),将备份服务器中的缓存同步到主服务器  
  139.        /// </summary>  
  140.        /// <returns></returns>  
  141.         public bool RestoreCache()   
  142.         {  
  143.             bool result = false;  
  144.               
  145.             return result;  
  146.         }  
  147.     }  
  148. }  


 

 

好了,由于篇幅有限。本文就到此了。 本文出自blog.csdn.net/dinglang_2009 ,转载请注明出处


分享到:
评论

相关推荐

    负载均衡和session 共享(Nginx + Memcached + Tomcat)

    负载均衡是一种分布式系统架构技术,它的目的是将工作负载分配到多个计算资源上,以避免单点故障,提高系统响应速度和服务质量。在Web服务中,负载均衡通常通过分发网络流量到不同的服务器,确保没有单一服务器过载...

    Java集群与负载均衡

    1. **配合使用**:Java集群和负载均衡通常一起使用,集群提供高可用性和可扩展性,负载均衡器负责分配请求,确保无单点故障。 2. **故障检测和恢复**:负载均衡器监控后端服务器状态,一旦发现故障,能迅速将请求...

    大型网站架构系列:负载均衡详解

    负载均衡是指在多台服务器之间分配网络负载的技术,其主要目标是避免单点故障,提高系统容错性,确保用户请求能够快速且无中断地得到处理。此外,通过合理分配工作负载,还可以充分利用硬件资源,防止过载,提升整体...

    最新Nginx1.9.4+Tomcat+Memcached负载均衡配置.rar

    当Nginx接收到请求后,它会将请求路由到负载最轻的Tomcat实例,从而避免单点故障,同时通过分散工作负载来提升整体性能。 Memcached是一种分布式内存对象缓存系统,用于减少数据库访问的负载。在Nginx+Tomcat架构中...

    高并发和负载均衡

    而负载均衡则是在系统层面确保服务的稳定性和高效性,通过多种策略将流量分散到多个服务器,降低单点故障的风险。在实际项目中,这两者通常是相辅相成,共同构建出能够应对大规模用户访问的健壮系统。

    高并发系统架构(LVS负载均衡、Nginx、共享存储、队列缓存)13.memcached缓存编程实战 共12页.pptx

    Nginx可以根据IP地址、URL、负载状态等多种因素,智能地分发请求至不同的后端服务器,确保每一台服务器都被高效利用,同时降低单点故障的风险。此外,Nginx对静态资源的处理能力尤为突出,可以有效减轻后端应用...

    网站负载均衡解决方案.doc

    16. 负载均衡的缺点:可能会出现“单点故障”的问题,如果挂了,会带来很多的麻烦。 17. 负载均衡的应用场景:适用于大型网站、电子商务平台、社交媒体平台等需要高可用性和高性能的应用场景。 18. 负载均衡的架构...

    apache2.2 tomcat6.0 集群与负载均衡

    总结来说,Apache 2.2 与 Tomcat 6.0 集群和负载均衡的配置是一项复杂但至关重要的任务,涉及到服务器复制、会话共享、负载均衡策略以及故障恢复等多个环节。通过精心设计和实施,可以构建出稳定、高效的Web服务环境...

    linux下配置tomcat集群的负载均衡.zip

    负载均衡是一种分布式系统架构,通过将工作负载分布到多个计算资源(如服务器)上来避免单点故障,提高响应时间和整体吞吐量。在Tomcat集群中,这通常通过网络负载均衡器(如Nginx、HAProxy或Apache HTTP Server)来...

    Nginx反向代理与负载均衡

    - **应用场景**:负载均衡主要用于提高网站或应用程序的访问速度和响应效率,同时也能有效防止单点故障。 #### 二、Nginx 原理 Nginx 之所以能够高效处理高并发请求,主要得益于以下几个方面: - **事件驱动模型*...

    memcached session共享所用到的jar包

    在Java Web应用中,结合memcached进行session共享可以有效地支持负载均衡,确保用户体验的一致性,同时避免了单点故障的风险。 总结来说,memcached session共享是解决分布式环境中会话一致性问题的有效方法。通过...

    Apache2.2 tomcat-6.0.18负载均衡与集群

    负载均衡是将接收到的网络流量分发到多个服务器上的技术,目的是提高系统的可用性,防止单点故障,同时也能提升整体处理能力。在Apache2.2中,可以通过mod_proxy模块实现反向代理负载均衡。该模块允许Apache将请求...

    nginx-tomcat6_session共享Memcached包.zip

    这种方法不仅提高了系统的可用性和可扩展性,还降低了单点故障的风险。然而,需要注意的是,随着系统规模的增长,必须考虑Session数据的安全性、备份和恢复策略,以及Memcached的性能监控和调优。

    memcached+tomcat的session共享

    - 降低单点故障:如果某台Tomcat服务器宕机,session数据不会丢失,因为它们在memcached中是持久化的。 然而,这种方式也存在一些潜在的问题: - 安全性:session数据在内存中存储,如果memcached被攻击,可能会...

    负载均衡必须要考虑的八个方案三终版.pdf

    6. **负载均衡**:负载均衡是解决高并发和大规模请求的核心技术,它通过将请求分发到多台服务器,避免单点故障,提高可用性和响应时间。常见的负载均衡算法包括轮询、权重轮询、最少连接数等。可以采用硬件负载均衡...

    nginx+tomcat+memcached

    这种解决方案不仅提高了系统的可扩展性,还降低了单点故障的风险。在实际操作中,还可以根据具体需求进行更复杂的配置和优化,例如使用一致性哈希算法进行负载均衡,或者结合Redis等其他缓存系统来进一步提升性能。

    MemCached Cache Java Client封装优化历程.docx

    这种方式避免了单点故障,提高了系统的可用性。 3. **分布式扩展**:Memcached 可以轻松地通过增加更多的服务器来扩展存储容量。客户端负责数据的分片和路由,使得对用户透明。 4. **Socket 通信**:Memcached ...

    nginx tomcat memcached 集群 session共享

    在构建高可用性和可扩展性的Web服务时,集群和会话共享是两个关键点。这里我们讨论的主题是如何通过Nginx、Tomcat和Memcached来...这种架构在处理高并发访问时尤为有效,同时降低了单点故障的风险,提高了用户体验。

    分布式缓存客户端MemcachedProviders最新下载

    3. **负载均衡**:数据分布在多个服务器上,可以分散请求,减轻单点压力。 4. **减少数据库负载**:缓存减少了对数据库的读写操作,有助于保护昂贵的数据库资源。 在具体使用MemcachedProviders时,开发人员需要...

Global site tag (gtag.js) - Google Analytics