10个Memcached节点,填充100000数据的测试结果如下:
192.168.1.199:11211 => 13046
192.168.1.191:11211 => 18230
192.168.1.197:11211 => 6382
192.168.1.194:11211 => 5242
192.168.1.193:11211 => 25554
192.168.1.190:11211 => 1124
192.168.1.196:11211 => 4074
192.168.1.198:11211 => 466
192.168.1.192:11211 => 24652
192.168.1.195:11211 => 1230
Add Server => 192.168.1.200:11211
192.168.1.199:11211 => 13046
192.168.1.191:11211 => 18230
192.168.1.197:11211 => 6382
192.168.1.194:11211 => 5242
192.168.1.193:11211 => 25554
192.168.1.190:11211 => 1124
192.168.1.196:11211 => 4074
192.168.1.198:11211 => 466
192.168.1.200:11211 => 273
192.168.1.192:11211 => 24379
192.168.1.195:11211 => 1230
Delete Server => 192.168.1.193:11211
192.168.1.199:11211 => 13046
192.168.1.191:11211 => 18230
192.168.1.197:11211 => 6382
192.168.1.194:11211 => 5242
192.168.1.190:11211 => 1124
192.168.1.196:11211 => 4074
192.168.1.198:11211 => 466
192.168.1.200:11211 => 273
192.168.1.192:11211 => 24379
192.168.1.195:11211 => 26784
单独Memcached节点不要存放太多的数据,一旦出现问题,会出现太多的缓存失效,所以如果发现某个Memcached节点存放了太多的数据,也许应该考虑将数据平衡到其它Memcached节点。
//程序代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Security.Cryptography;
namespace ConsoleApplication1
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Hashtable htServers = new Hashtable();
htServers.Add("192.168.1.190:11211", 0);
htServers.Add("192.168.1.191:11211", 0);
htServers.Add("192.168.1.192:11211", 0);
htServers.Add("192.168.1.193:11211", 0);
htServers.Add("192.168.1.194:11211", 0);
htServers.Add("192.168.1.195:11211", 0);
htServers.Add("192.168.1.196:11211", 0);
htServers.Add("192.168.1.197:11211", 0);
htServers.Add("192.168.1.198:11211", 0);
htServers.Add("192.168.1.199:11211", 0);
ConsistentHashing ch = new ConsistentHashing(htServers);
for (int i = 0; i < 100000; ++i)//填充数据
{
KeyValuePair<uint, string> Temp = ch.Locate(i.ToString());
int iCounter = Convert.ToInt32(htServers[Temp.Value]);
htServers[Temp.Value] = iCounter + 1;//计数器
}
IDictionaryEnumerator Iterator0 = htServers.GetEnumerator();
while (Iterator0.MoveNext())
{
Console.WriteLine("{0} => {1}", Iterator0.Key, Iterator0.Value);
}
string strAddServer = "192.168.1.200:11211";
htServers.Add(strAddServer, 0);
ch.AddServer(strAddServer);
Console.WriteLine("");
Console.WriteLine("Add Server => " + strAddServer);
Console.ReadLine();
htServers["192.168.1.190:11211"] = 0;
htServers["192.168.1.191:11211"] = 0;
htServers["192.168.1.192:11211"] = 0;
htServers["192.168.1.193:11211"] = 0;
htServers["192.168.1.194:11211"] = 0;
htServers["192.168.1.195:11211"] = 0;
htServers["192.168.1.196:11211"] = 0;
htServers["192.168.1.197:11211"] = 0;
htServers["192.168.1.198:11211"] = 0;
htServers["192.168.1.199:11211"] = 0;
htServers[strAddServer] = 0;
for (int i = 0; i < 100000; ++i)
{
KeyValuePair<uint, string> Temp = ch.Locate(i.ToString());
int iCounter = Convert.ToInt32(htServers[Temp.Value]);
htServers[Temp.Value] = iCounter + 1;
}
IDictionaryEnumerator Iterator1 = htServers.GetEnumerator();
while (Iterator1.MoveNext())
{
Console.WriteLine("{0} => {1}", Iterator1.Key, Iterator1.Value);
}
string strDeleteServer = "192.168.1.193:11211";
htServers.Remove(strDeleteServer);
ch.DeleteServer(strDeleteServer);
Console.WriteLine("");
Console.WriteLine("Delete Server => " + strDeleteServer);
Console.ReadLine();
htServers["192.168.1.190:11211"] = 0;
htServers["192.168.1.191:11211"] = 0;
htServers["192.168.1.192:11211"] = 0;
//htServers["192.168.1.193:11211"] = 0;
htServers["192.168.1.194:11211"] = 0;
htServers["192.168.1.195:11211"] = 0;
htServers["192.168.1.196:11211"] = 0;
htServers["192.168.1.197:11211"] = 0;
htServers["192.168.1.198:11211"] = 0;
htServers["192.168.1.199:11211"] = 0;
htServers[strAddServer] = 0;
for (int i = 0; i < 100000; ++i)
{
KeyValuePair<uint, string> Temp = ch.Locate(i.ToString());
int iCounter = Convert.ToInt32(htServers[Temp.Value]);
htServers[Temp.Value] = iCounter + 1;
}
IDictionaryEnumerator Iterator2 = htServers.GetEnumerator();
while (Iterator2.MoveNext())
{
Console.WriteLine("{0} => {1}", Iterator2.Key, Iterator2.Value);
}
Console.ReadLine();
}
}
/// <summary>
/// 一致性 Hash 算法
/// </summary>
class ConsistentHashing
{
List<KeyValuePair<uint, string>> _serList = new List<KeyValuePair<uint, string>>();
public ConsistentHashing(string[] serList)
{
if (serList == null || serList.Length == 0)
return;
foreach (var ser in serList)
{
uint uiKey = Hash(ser);
_serList.Add(new KeyValuePair<uint, string>(uiKey, ser));
}
_serList.Sort((x, y) => x.Key > y.Key ? 1 : (x.Key == y.Key ? 0 : -1));
}
public ConsistentHashing(Hashtable htServers)
{
IDictionaryEnumerator Iterator = htServers.GetEnumerator();
while (Iterator.MoveNext())
{
uint uiKey = Hash(Iterator.Key.ToString());
_serList.Add(new KeyValuePair<uint, string>(uiKey, Iterator.Key.ToString()));
}
_serList.Sort((x, y) => x.Key > y.Key ? 1 : (x.Key == y.Key ? 0 : -1));
}
public void AddServer(string strServer)
{
uint uiKey = Hash(strServer);
_serList.Add(new KeyValuePair<uint, string>(uiKey, strServer));
_serList.Sort((x, y) => x.Key > y.Key ? 1 : (x.Key == y.Key ? 0 : -1));
}
public void DeleteServer(string strServer)
{
uint uiKey = Hash(strServer);
_serList.Remove(new KeyValuePair<uint, string>(uiKey, strServer));
_serList.Sort((x, y) => x.Key > y.Key ? 1 : (x.Key == y.Key ? 0 : -1));
}
/// <summary>
/// Hash 具体的节点
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public KeyValuePair<uint, string> Locate(string key)
{
if (string.IsNullOrEmpty(key))
return _serList[0];
var hash = Hash(key);
var index = _serList.BinarySearch(new KeyValuePair<uint, string>(hash, null), new KeyValuePairComparer());
if (index >= 0)
return _serList[index];
else
{
var nIndex = ~index;
if (nIndex >= _serList.Count)
return _serList[0];
else
return _serList[nIndex];
}
}
/// <summary>
/// 得到字符串的HASH
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
private static uint Hash(string key)
{
return BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(Encoding.UTF8.GetBytes(key)), 0);
}
/// <summary>
/// KeyValuePair 比较器实现
/// 只比对 Key 部分大小
/// </summary>
class KeyValuePairComparer
: IComparer<KeyValuePair<uint, string>>
{
#region IComparer<KeyValuePair<uint,string>> 成员
public int Compare(KeyValuePair<uint, string> x, KeyValuePair<uint, string> y)
{
int i = x.Key > y.Key ? 1 : (x.Key == y.Key ? 0 : -1);
return i;
}
#endregion
}
#region HASH函数相关
/// <summary>
/// 修改版FNV1_32 哈希函数
/// </summary>
/// <remarks>
/// FNV1_32 哈希函数
/// Fowler-Noll-Vo hash, variant 1, 32-bit version.
/// http://www.isthe.com/chongo/tech/comp/fnv/
/// 修改版FNV1_32 哈希函数
/// Modified Fowler-Noll-Vo hash, 32-bit version.
/// http://home.comcast.net/~bretm/hash/6.html
/// </remarks>
class ModifiedFNV1_32 : HashAlgorithm
{
private static readonly uint FNV_prime = 16777619;
private static readonly uint offset_basis = 2166136261;
protected uint hash;
public ModifiedFNV1_32()
{
HashSizeValue = 32;
}
public override void Initialize()
{
hash = offset_basis;
}
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
int length = ibStart + cbSize;
for (int i = ibStart; i < length; i++)
{
hash = (hash * FNV_prime) ^ array[i];
}
}
protected override byte[] HashFinal()
{
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return BitConverter.GetBytes(hash);
}
}
#endregion
}
}