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

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

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

  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. string keybase = "test";
  22. //if (mc.Get(keybase) == null)
  23. //{
  24. //尝试添加数据
  25. #region 单个key的情况,value值增大,数据不会自动分布,全都集中在一台服务器上
  26. //List<int> list = new List<int>();
  27. //for (int i = 0; i < 100; i++)
  28. //{
  29. //    list.Add(i);
  30. //}
  31. //bool reslut =  mc.Add("test", list);
  32. //if (reslut)
  33. //{
  34. //    Response.Write("Add cache success");
  35. //}
  36. #endregion
  37. #region 多个key的情况,数据会自动均衡的分布  三台服务器 33%,33%,34%
  38. //for (int i = 0; i < 100; i++)
  39. //{
  40. //    bool result = mc.Add(keybase + i, i);
  41. //    if (!result) {
  42. //        Response.Write("Add cache faild");
  43. //    }
  44. //}
  45. #endregion
  46. // }
  47. // else {
  48. //object value = mc.Get("test");
  49. int count = 0;
  50. for (int i = 0; i < 100; i++)
  51. {
  52. object value = mc.Get(keybase + i);
  53. if(value!=null)
  54. {
  55. ++count;
  56. }
  57. }
  58. Response.Write("服务器存储数据量:"+count);
  59. //   }
  60. pool.Shutdown();
  61. }

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

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

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

  1. libevent带负载均衡的多线程使用示例

    功能: 主线程根据负载工作线程负载均衡算法,每隔一秒钟向特定的工作线程发送一条字符串信息,工作线程简单的把字符串信息打开出来.   Makefile   eventtest : eventtest.c ...

  2. 干货 | 亿级Web系统负载均衡几种实现方式

    一个执着于技术的公众号 负载均衡(Load Balance)是集群技术(Cluster)的一种应用技术.负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力.目前最常见的负载均衡应用是Web ...

  3. 终于等到你---订餐系统之负载均衡(nginx+memcached+ftp上传图片+iis)

    又见毕业 对面工商大学的毕业生叕在拍毕业照了,一个个脸上都挂满了笑容,也许是满意自己四年的修行,也许是期待步入繁华的社会... 恰逢其时的连绵细雨与满天柳絮,似乎也是在映衬他们心中那些离别的忧伤,与对 ...

  4. Nginx+Tomcat+Memcached负载均衡集群服务搭建

    操作系统:CentOS6.5  本文档主要讲解,如何在CentOS6.5下搭建Nginx+Tomcat+Memcached负载均衡集群服务器,Nginx负责负载均衡,Tomcat负责实际服务,Memc ...

  5. nginx+tomcat+memcached搭建服务器集群及负载均衡

    在实际项目中,由于用户的访问量很大的原因,往往需要同时开启多个服务器才能满足实际需求.但是同时开启多个服务又该怎么管理他们呢?怎样实现session共享呢?下面就来讲一讲如何使用tomcat+ngin ...

  6. windows使用nginx+memcached实现负载均衡和session或者缓存共享

    windows使用nginx+memcached实现负载均衡和session或者缓存共享 两台server server1:115.29.186.215 windows2008 64位操作系统 ser ...

  7. Nginx + Memcached 实现Session共享的负载均衡

    session共享 我们在做站点的试试,通常需要保存用户的一些基本信息,比如登录就会用到Session:当使用Nginx做负载均衡的时候,用户浏览站点的时候会被分配到不同的服务器上,此时如果登录后Se ...

  8. 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 ...

  9. Nginx+Tomcat8+Memcached实现负载均衡及session共享

    1> 基础环境 简易拓扑图: 2> 部署Tomcat [root@node01 ~]# ll -h ~ |egrep 'jdk|tomcat'-rw-r--r-- 1 root root ...

随机推荐

  1. Xcode和github入门详细教程

    Xcode和github详细教程! 主要是参考了现在网上的一些资料给没整过的人一个详细的指南. (1)先在github上注册账号,自行解决! (2)在导航栏右上角new一个repository(仓库) ...

  2. C# 5.0 TAP 模式下的HTTP Get和Post

    标题有点瘆人,换了工作之后很少写代码了,之前由于签了保密协议,不敢把代码拿出来分享给大家,只能摘抄网上的, 今斗胆拿出来晒晒,跪求指点,直接上代码吧 public class HTTPHelper : ...

  3. leetcode 练习1 two sum

    leetcode 练习1  two sum whowhoha@outlook.com 问题描述 Given an array of integers, return indices of the tw ...

  4. python调试总结

    调试通常采用两种方式,打印日志调试以及运行时实时跟踪调试. 一.打印日志: 1. print不要看不起print,这是一切调试的起点,即便是调试Java或者C这种巨麻烦的编译语言,print仍然是常用 ...

  5. ***SQL统计语句总结(运用场景:运营分析,财务分析等)

    -- 统计三月的每天的数据量 ,) ,) ; --统计从5月19到6月29的数据量 , ) AS '日期', count(*) AS '医说数' FROM xm_feed a WHERE a.feed ...

  6. saltstack远程操作WINDOWS的POWERSHELL脚本

    这个东东,花了两天来查找资料和测试,终于算是搞定.作记录: 直接在MASTER上执行的命令: salt '*' cmd.script salt://scripts/windows_task.ps1 a ...

  7. hdu 2147 kiki's game 博弈论

    找规律的博弈论!! 很容易发现当n,m都为奇数时先手必败! 代码如下: #include<iostream> #include<stdio.h> #define I(x,y) ...

  8. linux ps查看进程命令

    linux ps查看进程命令ps命令作用:将某个时间点的程序运作情况撷取下来 实例: [root@linux ~]# ps aux [root@linux ~]# ps -lA [root@linux ...

  9. C++常用语法

    unordered_map<int, Node*> mp; if (mp.find(key) == mp.end()) unordered_map<int, Node*>::i ...

  10. Java Web开发 之小张老师总结GET和POST区别

    get和post区别1.传输方式不同,get在request-line中传输(即在URL中传输).post在request-line及 request-body中传输(可认为隐藏传输)2.get传输长 ...