class KetamaNodeLocator
{
private Dictionary<long, RedisCluster> ketamaNodes;
private HashAlgorithm hashAlg;
private int numReps = 160;
private long[] keys;
public KetamaNodeLocator(List<RedisCluster> nodes)
{
ketamaNodes = new Dictionary<long, RedisCluster>();
//对所有节点,生成nCopies个虚拟结点
for (int j = 0; j < nodes.Count; j++)
{
RedisCluster node = nodes[j];
int numReps = node.Weight;
//每四个虚拟结点为一组
for (int i = 0; i < numReps / 4; i++)
{
byte[] digest = ComputeMd5(
String.Format("{0}_{1}_{2}", node.RoleName, node.RouteValue, i));
/** Md5是一个16字节长度的数组,将16字节的数组每四个字节一组,
* 分别对应一个虚拟结点,这就是为什么上面把虚拟结点四个划分一组的原因*/
for (int h = 0; h < 4; h++)
{
long rv = ((long)(digest[3 + h * 4] & 0xFF) << 24)
| ((long)(digest[2 + h * 4] & 0xFF) << 16)
| ((long)(digest[1 + h * 4] & 0xFF) << 8)
| ((long)digest[0 + h * 4] & 0xFF);
rv = rv & 0xffffffffL; /* Truncate to 32-bits */
ketamaNodes[rv] = node;
}
}
}
keys = ketamaNodes.Keys.OrderBy(p => p).ToArray();
}
public RedisCluster GetWorkerNode(string k)
{
byte[] digest = ComputeMd5(k);
return GetNodeInner(Hash(digest, 0));
}
RedisCluster GetNodeInner(long hash)
{
if (ketamaNodes.Count == 0)
return null;
long key = hash;
int near = 0;
int index = Array.BinarySearch(keys, hash);
if (index < 0)
{
near = (~index);
if (near == keys.Length)
near = 0;
}
else
{
near = index;
}
return ketamaNodes[keys[near]];
}
public static long Hash(byte[] digest, int nTime)
{
long rv = ((long)(digest[3 + nTime * 4] & 0xFF) << 24)
| ((long)(digest[2 + nTime * 4] & 0xFF) << 16)
| ((long)(digest[1 + nTime * 4] & 0xFF) << 8)
| ((long)digest[0 + nTime * 4] & 0xFF);
return rv & 0xffffffffL; /* Truncate to 32-bits */
}
public static byte[] ComputeMd5(string k)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] keyBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(k));
md5.Clear();
return keyBytes;
}
}