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