using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; namespace Sodao.FastSocket.SocketBase.Utils { /// /// 一致性哈希container /// /// public sealed class ConsistentHashContainer { #region Private Members private readonly Dictionary _dic = new Dictionary(); private readonly T[] _arr = null; private readonly uint[] _keys = null; #endregion #region Constructors /// /// new /// /// /// source is null public ConsistentHashContainer(IDictionary source) { if (source == null) throw new ArgumentNullException("source"); var servers = new List(); var keys = new List(); foreach (var child in source) { for (int i = 0; i < 250; i++) { uint key = BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(Encoding.ASCII.GetBytes(child.Key + "-" + i)), 0); if (!this._dic.ContainsKey(key)) { this._dic[key] = child.Value; keys.Add(key); } } servers.Add(child.Value); } this._arr = servers.ToArray(); keys.Sort(); this._keys = keys.ToArray(); } #endregion #region Private Methods /// /// Given an item key hash, /// this method returns the Server which is closest on the server key continuum. /// /// /// private T Get(uint consistentKey) { int i = Array.BinarySearch(this._keys, consistentKey); //If not exact match... if (i < 0) { //Get the index of the first item bigger than the one searched for. i = ~i; //If i is bigger than the last index, it was bigger than the last item = use the first item. if (i >= this._keys.Length) i = 0; } return this._dic[this._keys[i]]; } #endregion #region Public Methods /// /// get /// /// /// public T Get(byte[] consistentKey) { if (this._arr.Length == 0) return default(T); //Quick return if we only have one. if (this._arr.Length == 1) return this._arr[0]; return Get(BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(consistentKey), 0)); } #endregion #region FNV1_32 /// /// Fowler-Noll-Vo hash, variant 1, 32-bit version. /// http://www.isthe.com/chongo/tech/comp/fnv/ /// public class FNV1_32 : HashAlgorithm { private static readonly uint FNV_prime = 16777619; private static readonly uint offset_basis = 2166136261; /// /// hash /// protected uint hash; /// /// new /// public FNV1_32() { HashSizeValue = 32; } /// /// init /// public override void Initialize() { hash = offset_basis; } /// /// hashcore /// /// /// /// 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]; } /// /// hash final /// /// protected override byte[] HashFinal() { return BitConverter.GetBytes(hash); } } #endregion #region ModifiedFNV1_32 /// /// Modified Fowler-Noll-Vo hash, 32-bit version. /// http://home.comcast.net/~bretm/hash/6.html /// public class ModifiedFNV1_32 : FNV1_32 { /// /// hashFinal. /// /// protected override byte[] HashFinal() { hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; return BitConverter.GetBytes(hash); } } #endregion } }