设为首页 收藏本站
查看: 1040|回复: 0

[经验分享] 分析Memcached客户端如何把缓存数据分布到多个服务器上

[复制链接]

尚未签到

发表于 2015-8-31 12:35:16 | 显示全部楼层 |阅读模式
  Memcached客户端可以设多个memcached服务器,它是如何把数据分发到各个服务器上,而使各个服务器负载平衡的呢?
  可以看看.net版中的客户端中的源码,就可以知道 先看代码:
  

DSC0000.gif DSC0001.gif 获取Socket连接代码
  1 /// <summary>
  2         /// Returns appropriate SockIO object given
  3         /// string cache key and optional hashcode.
  4         ///
  5         /// Trys to get SockIO from pool.  Fails over
  6         /// to additional pools in event of server failure.
  7         /// </summary>
  8         /// <param name="key">hashcode for cache key</param>
  9         /// <param name="hashCode">if not null, then the int hashcode to use</param>
10         /// <returns>SockIO obj connected to server</returns>
11         public SockIO GetSock(string key, object hashCode)
12         {
13             string hashCodeString = "<null>";
14             if(hashCode != null)
15                 hashCodeString = hashCode.ToString();
16
17             if(Log.IsDebugEnabled)
18             {
19                 Log.Debug(GetLocalizedString("cache socket pick").Replace("$$Key$$", key).Replace("$$HashCode$$", hashCodeString));
20             }
21
22             if (key == null || key.Length == 0)
23             {
24                 if(Log.IsDebugEnabled)
25                 {
26                     Log.Debug(GetLocalizedString("null key"));
27                 }
28                 return null;
29             }
30
31             if(!_initialized)
32             {
33                 if(Log.IsErrorEnabled)
34                 {
35                     Log.Error(GetLocalizedString("get socket from uninitialized pool"));
36                 }
37                 return null;
38             }
39
40             // if no servers return null
41             if(_buckets.Count == 0)
42                 return null;
43
44             // if only one server, return it
45             if(_buckets.Count == 1)
46                 return GetConnection((string)_buckets[0]);
47
48             int tries = 0;
49
50             // generate hashcode
51             int hv;
52             if(hashCode != null)
53             {
54                 hv = (int)hashCode;
55             }
56             else
57             {
58
59                 // NATIVE_HASH = 0
60                 // OLD_COMPAT_HASH = 1
61                 // NEW_COMPAT_HASH = 2
62                 switch(_hashingAlgorithm)
63                 {
64                     case HashingAlgorithm.Native:
65                         hv = key.GetHashCode();
66                         break;
67
68                     case HashingAlgorithm.OldCompatibleHash:
69                         hv = OriginalHashingAlgorithm(key);
70                         break;
71
72                     case HashingAlgorithm.NewCompatibleHash:
73                         hv = NewHashingAlgorithm(key);
74                         break;
75
76                     default:
77                         // use the native hash as a default
78                         hv = key.GetHashCode();
79                         _hashingAlgorithm = HashingAlgorithm.Native;
80                         break;
81                 }
82             }
83
84             // keep trying different servers until we find one
85             while(tries++ <= _buckets.Count)
86             {
87                 // get bucket using hashcode
88                 // get one from factory
89                 int bucket = hv % _buckets.Count;
90                 if(bucket < 0)
91                     bucket += _buckets.Count;
92
93                 SockIO sock = GetConnection((string)_buckets[bucket]);
94
95                 if(Log.IsDebugEnabled)
96                 {
97                     Log.Debug(GetLocalizedString("cache choose").Replace("$$Bucket$$", _buckets[bucket].ToString()).Replace("$$Key$$", key));
98                 }
99
100                 if(sock != null)
101                     return sock;
102
103                 // if we do not want to failover, then bail here
104                 if(!_failover)
105                     return null;
106
107                 // if we failed to get a socket from this server
108                 // then we try again by adding an incrementer to the
109                 // current key and then rehashing
110                 switch(_hashingAlgorithm)
111                 {
112                     case HashingAlgorithm.Native:
113                         hv += ((string)("" + tries + key)).GetHashCode();
114                         break;
115
116                     case HashingAlgorithm.OldCompatibleHash:
117                         hv += OriginalHashingAlgorithm("" + tries + key);
118                         break;
119
120                     case HashingAlgorithm.NewCompatibleHash:
121                         hv += NewHashingAlgorithm("" + tries + key);
122                         break;
123
124                     default:
125                         // use the native hash as a default
126                         hv += ((string)("" + tries + key)).GetHashCode();
127                         _hashingAlgorithm = HashingAlgorithm.Native;
128                         break;
129                 }
130             }
131
132             return null;
133         }
134   
  上面代码是代码文件SockIOPool.cs中的一个方法,从方法签名上可以看出,获取一个socket连接是根据需要缓存数据的唯一键和它的哈希值,因为缓存的数据的键值是唯一的,所以它的哈希代码也是唯一的;
  再看看上面方法中的以下代码:
         int bucket = hv % _buckets.Count;

                if(bucket < 0)

                    bucket += _buckets.Count;


                SockIO sock = GetConnection((string)_buckets[bucket]);

    具体的选择服务器的算法是:唯一键值的哈希值与存放服务器列表中服务器(服务器地址记录不是唯一的)的数量进行模数运算来选择服务器的地址的。所以数据缓存在那台服务器取决于缓存数据的唯一键值所产生的哈希值和存放服务器列表中服务器的数量值,所以访问memcached服务的所有客户端操作数据时都必须使用同一种哈希算法和相同的服务器列表配置,否则就会或取不到数据或者重复存取数据。由于不同数据的唯一键所对应的哈希值不同,所以不同的数据就有可能分散到不同的服务器上,达到多个服务器负载平衡的目的。
  如果几台服务器当中,负载能力各不同,想根据具体情况来配置各个服务器负载作用,也是可以做到的。看上面代码,可以知道程序是从_buckets中获取得服务器地址的,_buckets存放着服务器的地址信息,服务器地址在_bucket列表中并不是唯一的,它是可以有重复记录的。相同的服务器地址在_bucket重复记录越多,它被选中的机率就越大,相应负载作用也就越大。
  怎么设置服务器让它发挥更大的负载作用,如下面代码:            
          String[] serverlist = {"192.168.1.2:11211", "192.168.1.3:11211"};
          int[] weights   = new int[]{5, 2};
          SockIOPool pool = SockIOPool.GetInstance();
          pool.SetServers(serverlist);
          pool.SetWeights(weights);   
           pool.Initialize();
      pool.SetWeights(weights)方法就是设配各个服务器负载作用的系数,系数值越大,其负载作用也就越大。如上面的例子,就设服务器192.168.1.2的负载系数为5,服务器192.168.1.3的负载系数为2,也就说服务器192.168.1.2 比192.168.1.3的负载作用大。      
  程序中根据缓存数据中的唯一键标识的哈希值跟服务器列表中服务器记录数量求模运算来确定数据的缓存的位置的方法,算法的优点:能够把数据匀均的分散到各个服务器上数据服务器负载平衡,当然也可以通过配置使不同服务器有不同的负载作用。但也有缺点:使同类的数据过于分散,同个模块的数据都分散到不同的数据,不好统一管理和唯护;比如:现在有A、B、C、D四台服务器一起来做缓存服务器,数月后C台服务器突然死掉不可用啦,那么按算法缓存在C台服务器的数据都不可用啦,但客户端还是按原来的四台服务器的算法来取操作数据,所以分布在C服务上的数据在C服务器恢复可用之前都不可用,都必须从数据库中读取数据,并且不能添加到缓存中,因为只要缓存数据的Key不变,它还是会被计算分配到C服务器上。如果想把分配到C服务器就必须全部初始化A、B、D三台服务器上的所有数据,并把C服务器从服务器列表中移除。
  如果我们能够把数据分类分布到各个服务器中,同类型的数据分布到相同的服务器;比如说,A服务器存放用户日志模块信息,B服务器存放用户相册模块信息,C服务器存放音乐模块信息,D服务器存放用户基础信息。如果C服务器不可用后,就可以更改下配置使它存放在其它服务器当中,而且并不影响其它服务器的缓存信息。
  解决方法1:不同的模块使用不同memcached客户端实例,这样不同模块就可以配置不同的服务器列表,这样不同模块的数据就缓存到了不同的服务器中。这样,当某台服务器不可用后,只会影响到相应memcached客户端实例的数据,而不会影响到其它客户端实例的数据。
  解决方法2:修改或添加新的算法,并在数据唯一键中添加命名空间,算法根据配置和数据唯一键中命名空间来选择不同的Socket连接,也就是服务器啦。
  数据项唯一键(key)的定义:命名空间.数据项ID,就跟编程中的&#8221; 命名空间&#8221;一样,经如说用户有一篇日志的ID是&#8221;999999&#8221;, 那么这条篇日志的唯一键就是:Sns.UserLogs.Log.999999,当然我们存贮的时候考虑性能问题,可以用一个短的数值来代替命名空间。这样在选择Socket的时候就可以根据数据项中的唯一键来选择啦。
  刚入门,请各位大佬多多指导!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-106811-1-1.html 上篇帖子: 如何在Windows平台下安装Memcached 下篇帖子: 由Memcached使用不当而引发性能问题的两个经验总结
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表