From 49d5fc2662a5e671a51009e5b40a1311a9f440ee Mon Sep 17 00:00:00 2001 From: Petrichor <390983386@qq.com> Date: Wed, 6 Apr 2022 11:14:55 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Day4=20=E4=B8=80=E8=87=B4?= =?UTF-8?q?=E6=80=A7=E5=93=88=E5=B8=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consistenthash/consistenthash.go | 39 +++++++++++++++++++- consistenthash/consistenthash_test.go | 53 +++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 consistenthash/consistenthash_test.go diff --git a/consistenthash/consistenthash.go b/consistenthash/consistenthash.go index 65c7e87..964fb2a 100644 --- a/consistenthash/consistenthash.go +++ b/consistenthash/consistenthash.go @@ -1,12 +1,18 @@ package consistenthash -import "hash/crc32" +import ( + "fmt" + "hash/crc32" + "sort" + "strconv" +) type Hash func(data []byte) uint32 +// Map constains all hashed keys type Map struct { hash Hash - replicas int //虚拟节点倍数 + replicas int // 虚拟节点倍数 keys []int // 哈希环 hashMap map[int]string // 虚拟节点与真实节点的映射表,键是哈希值,值是真实节点的名称 } @@ -22,3 +28,32 @@ func New(replicas int, fn Hash) *Map { } return m } + +// Add adds some keys to the hash. +// 传入真实节点名称 +func (m *Map) Add(keys ...string) { + for _, key := range keys { + for i := 0; i < m.replicas; i++ { + hash := int(m.hash([]byte(strconv.Itoa(i) + key))) + fmt.Printf("key: %v, i: %v, hash: %v\n", key, i, hash) + m.keys = append(m.keys, hash) + m.hashMap[hash] = key + } + } + sort.Ints(m.keys) +} + +func(m *Map) Get(key string) string { + if len(m.keys) == 0 { + return "" + } + + // 计算 key 的哈希值 + hash := int(m.hash([]byte(key))) + // 顺时针找到第一个匹配的虚拟节点的下标 idx,从 m.keys 中获取对应的哈希值 + idx := sort.Search(len(m.keys), func(i int) bool { + return m.keys[i] >= hash + }) + // 得到真实节点 + return m.hashMap[m.keys[idx%len(m.keys)]] +} \ No newline at end of file diff --git a/consistenthash/consistenthash_test.go b/consistenthash/consistenthash_test.go new file mode 100644 index 0000000..ec4e1ca --- /dev/null +++ b/consistenthash/consistenthash_test.go @@ -0,0 +1,53 @@ +package consistenthash + +import ( + "strconv" + "testing" +) + +func TestHashing(t *testing.T) { + hash := New(3, func(key []byte) uint32 { + i, _ := strconv.Atoi(string(key)) + return uint32(i) + }) + + // 哈希值: 2, 4, 6, 12, 14, 16, 22, 24, 26 + hash.Add("6", "4", "2") + // fmt.Printf("%+v\n", hash) + // &{hash:0x77f740 replicas:3 + // keys:[2 4 6 12 14 16 22 24 26] + // hashMap:map[2:2 4:4 6:6 12:2 14:4 16:6 22:2 24:4 26:6]} + + testCases := map[string]string{ + "2": "2", + "11": "2", + "23": "4", + "27": "2", + } + + for k, v := range testCases { + if hash.Get(k) != v { + t.Errorf("Asking for %s, should have yielded %s", k, v) + } + } + + hash.Add("8") + // fmt.Printf("%+v\n", hash) + // &{hash:0x5af740 replicas:3 + // keys:[2 4 6 8 12 14 16 18 22 24 26 28] + // hashMap:map[2:2 4:4 6:6 8:8 12:2 14:4 16:6 18:8 22:2 24:4 26:6 28:8]} + + testCases["27"] = "8" + + for k, v := range testCases { + if hash.Get(k) != v { + t.Errorf("Asking for %s, should have yielded %s", k, v) + } + } + + // hash.Add("9", "10", "11", "12", "233") + // fmt.Printf("%+v\n", hash) + // &{hash:0xf1f740 replicas:3 + // keys:[2 4 6 8 9 10 11 12 12 14 16 18 19 22 24 26 28 29 110 111 112 210 211 212] + // hashMap:map[2:2 4:4 6:6 8:8 9:9 10:10 11:11 12:12 14:4 16:6 18:8 19:9 22:2 24:4 26:6 28:8 29:9 110:10 111:11 112:12 210:10 211:11 212:12]} +}