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

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
}