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;
        }
    }
}

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

时间: 2024-11-08 21:16:05

memcached单点故障与负载均衡的相关文章

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

原文:大型网站架构系列:负载均衡详解(3) 软件负载均衡概述 Ngnix负载均衡 Lvs负载均衡 Haproxy负载均衡 本次分享总结 一.软件负载均衡概述 硬件负载均衡性能优越,功能全面,但是价格昂贵,一般适合初期或者土豪级公司长期使用.因此软件负载均衡在互联网领域大量使用.常用的软件负载均衡软件有Nginx,Lvs,HaProxy等.本文参考大量文档,部分为直接拷贝,参考出处见负载均衡详解(4). 二.Ngnix负载均衡 Ngnix是一款轻量级的Web服务器/反向代理服务器,工作在七层Htt

Nginx和Tomcat负载均衡实现session共享(转)

    以前的项目使用Nginx作为反向代理实现了多个Tomcat的负载均衡,为了实现多个Tomcat之间的session共享,使用了开源的Memcached-Session-Manager框架.     此框架的优势:          1.支持Tomcat6和Tomcat7           2.操作粘性或不黏性Session           3.没有单点故障           4.Tomcat的故障转移           5.Memcached的故障转移           6.

一张图看懂阿里云网络产品[五]负载均衡SLB

负载均衡SLB是对多台云服务器进行流量分发的服务.它可以通过流量分发扩展应用系统的服务能力,通过消除单点故障提升应用系统的可用性.通过设置虚拟IP,将位于同一地域的多台云服务器虚拟成一个高性能.高可用的应用服务池.根据指定的方式,将来自客户端的网络请求分发到云服务器池中. 系列文章持续更新中,敬请关注 [一]网络产品概览 [二]VPC [三]EIP [四]NAT网关 [五]负载均衡SLB [六]共享带宽 [七]共享流量包 [八]高速通道 [九]VPN网关 [十]云托付 [十一]全球加速

Windows 08 R2_NLB负载均衡(图文详解)

目录 目录 Load Balance 使用NLB来部署Web Farm集群 环境准备 在Win08r2pc1中配置DNS服务 在Win08r2pc1中部署File Service文件服务 在Win08r2pc1中部署IIS Web服务 在win08r2pc2上部署IIS Web服务 配置共享的站点文件夹 win08r2pc1中的web共享配置 win08r2pc2中的web共享配置 创建NLB集群 NLB的高级管理 编辑端口规则 Load Balance Load Balance:负载均衡提供了

【转载】四层和七层负载均衡的区别

简单理解四层和七层负载均衡:①所谓四层就是基于IP+端口的负载均衡:七层就是基于URL等应用层信息的负载均衡:同理,还有基于MAC地址的二层负载均衡和基于IP地址的三层负载均衡. 换句换说,二层负载均衡会通过一个虚拟MAC地址接收请求,然后再分配到真实的MAC地址:三层负载均衡会通过一个虚拟IP地址接收请求,然后再分配到真实的IP地址:四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器:七层通过虚拟的URL或主机名接收请求,然后再分配到真实的服务器. ②所谓的四到七层负载均衡,就是在对后台

Windows平台分布式架构实践:负载均衡

概述 最近.NET的世界开始闹腾了,微软官方终于加入到了对.NET跨平台的支持,并且在不久的将来,我们在VS里面写的代码可能就可以通过Mono直接在Linux和Mac上运行.那么大家(开发者和企业)为什么那么的迫切的希望.NET跨平台呢?第一个理由是便宜,淘宝号称4万多台服务器全部运行在Linux,Linux平台下还有免费的MySql,这些都是免费的,这些省下来直接就是利润呀,做企业的成本可以降低又没有任何损失,何乐而不为呢?第二个理由是在Linux系统下还有很多非常优秀的构架(当然同样也是免费

LVS+Keepalived构建高可用负载均衡的配置

一. LVS简介 LVS是Linux Virtual Server的简称,也就是Linux虚拟服务器, 是一个由 章文嵩博士发起的自由软件项目,它的官方站点是www.linuxvirtualserver.org .现在LVS已经是 Linux标准内核的一部分,在Linux2.4内核以前,使用LVS时必 须要重新编译内核以支持LVS功能模块,但是从Linux2.4内核以后,已经完全内置 了LVS的各个功能模块,无需给内核打任何补丁,可以直接使用LVS提供的各种功 能.使用LVS技术要达到的目标是:

Linux 负载均衡的基本概念

一.负载均衡的主要技术 1.Cisco以太网通道(网络均衡) 2.WindowsNLB技术 3.LinuxLVS技术 4.F5等负载均衡器(服务均衡) 二.冗余技术 1.CsicoHSRP热备份路由 2.Windows集群技术 3.LinuxHA集群技术 4.IBMAIX集群 下面主要解释一下负载均衡.集群.主从的区别 1.负载均衡:把客户端的请求通过负载均衡算法分发到不同的正常运行的服务器来处理,从而减少单个服务器的压力. 2.集群:集群主要是为了解决单点故障,防止当一个服务器宕机无法提供服务

四层和七层负载均衡的区别

  (一) 简单理解四层和七层负载均衡: ① 所谓四层就是基于IP+端口的负载均衡;七层就是基于URL等应用层信息的负载均衡;同理,还有基于MAC地址的二层负载均衡和基于IP地址的三层负载均衡. 换句换说,二层负载均衡会通过一个虚拟MAC地址接收请求,然后再分配到真实的MAC地址;三层负载均衡会通过一个虚拟IP地址接收请求,然后再分配到真实的IP地址;四层通过虚拟IP+端口接收请求,然后再分配到真实的服务器;七层通过虚拟的URL或主机名接收请求,然后再分配到真实的服务器. ② 所谓的四到七层负载