You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
2.2 KiB
136 lines
2.2 KiB
package balancer |
|
|
|
import ( |
|
"hash/fnv" |
|
"sort" |
|
"strconv" |
|
"sync" |
|
) |
|
|
|
type HashFunc func(data []byte) uint32 |
|
|
|
var ( |
|
DefaultReplicas = 10 |
|
Salt = "this_is_salt" |
|
) |
|
|
|
func DefaultHash(data []byte) uint32 { |
|
f := fnv.New32() |
|
_, err := f.Write(data) |
|
if err != nil { |
|
panic(err) |
|
} |
|
return f.Sum32() |
|
} |
|
|
|
type Ketama struct { |
|
sync.Mutex |
|
hash HashFunc |
|
replicas int |
|
keys []int // Sorted keys |
|
hashMap map[int]string |
|
} |
|
|
|
func NewKetama(replicas int, fn HashFunc) *Ketama { |
|
h := &Ketama{ |
|
replicas: replicas, |
|
hash: fn, |
|
hashMap: make(map[int]string), |
|
} |
|
if h.replicas <= 0 { |
|
h.replicas = DefaultReplicas |
|
} |
|
if h.hash == nil { |
|
h.hash = DefaultHash |
|
} |
|
return h |
|
} |
|
|
|
func (h *Ketama) IsEmpty() bool { |
|
h.Lock() |
|
defer h.Unlock() |
|
|
|
return len(h.keys) == 0 |
|
} |
|
|
|
func (h *Ketama) Add(nodes ...string) { |
|
h.Lock() |
|
defer h.Unlock() |
|
|
|
for _, node := range nodes { |
|
for i := 0; i < h.replicas; i++ { |
|
key := int(h.hash([]byte(strconv.Itoa(i) + node + Salt))) |
|
|
|
if _, ok := h.hashMap[key]; !ok { |
|
h.keys = append(h.keys, key) |
|
} |
|
h.hashMap[key] = node |
|
} |
|
} |
|
sort.Ints(h.keys) |
|
} |
|
|
|
func (h *Ketama) Remove(nodes ...string) { |
|
h.Lock() |
|
defer h.Unlock() |
|
|
|
deletedKey := make([]int, 0) |
|
for _, node := range nodes { |
|
for i := 0; i < h.replicas; i++ { |
|
key := int(h.hash([]byte(strconv.Itoa(i) + node + Salt))) |
|
|
|
if _, ok := h.hashMap[key]; ok { |
|
deletedKey = append(deletedKey, key) |
|
delete(h.hashMap, key) |
|
} |
|
} |
|
} |
|
if len(deletedKey) > 0 { |
|
h.deleteKeys(deletedKey) |
|
} |
|
} |
|
|
|
func (h *Ketama) deleteKeys(deletedKeys []int) { |
|
sort.Ints(deletedKeys) |
|
|
|
index := 0 |
|
count := 0 |
|
for _, key := range deletedKeys { |
|
for ; index < len(h.keys); index++ { |
|
h.keys[index-count] = h.keys[index] |
|
|
|
if key == h.keys[index] { |
|
count++ |
|
index++ |
|
break |
|
} |
|
} |
|
} |
|
|
|
for ; index < len(h.keys); index++ { |
|
h.keys[index-count] = h.keys[index] |
|
} |
|
|
|
h.keys = h.keys[:len(h.keys)-count] |
|
} |
|
|
|
func (h *Ketama) Get(key string) (string, bool) { |
|
if h.IsEmpty() { |
|
return "", false |
|
} |
|
|
|
hash := int(h.hash([]byte(key + Salt))) |
|
|
|
h.Lock() |
|
defer h.Unlock() |
|
|
|
idx := sort.Search(len(h.keys), func(i int) bool { |
|
return h.keys[i] >= hash |
|
}) |
|
|
|
if idx == len(h.keys) { |
|
idx = 0 |
|
} |
|
str, ok := h.hashMap[h.keys[idx]] |
|
return str, ok |
|
}
|
|
|