| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- package hashring
- import (
- "crypto/md5"
- "fmt"
- "math"
- "sort"
- )
- type HashKey uint32
- type HashKeyOrder []HashKey
- func (h HashKeyOrder) Len() int { return len(h) }
- func (h HashKeyOrder) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
- func (h HashKeyOrder) Less(i, j int) bool { return h[i] < h[j] }
- type HashRing struct {
- ring map[HashKey]string
- sortedKeys []HashKey
- nodes []string
- weights map[string]int
- }
- func New(nodes []string) *HashRing {
- hashRing := &HashRing{
- ring: make(map[HashKey]string),
- sortedKeys: make([]HashKey, 0),
- nodes: nodes,
- weights: make(map[string]int),
- }
- hashRing.generateCircle()
- return hashRing
- }
- func NewWithWeights(weights map[string]int) *HashRing {
- nodes := make([]string, 0, len(weights))
- for node, _ := range weights {
- nodes = append(nodes, node)
- }
- hashRing := &HashRing{
- ring: make(map[HashKey]string),
- sortedKeys: make([]HashKey, 0),
- nodes: nodes,
- weights: weights,
- }
- hashRing.generateCircle()
- return hashRing
- }
- func (h *HashRing) Size() int {
- return len(h.nodes)
- }
- func (h *HashRing) UpdateWithWeights(weights map[string]int) {
- nodesChgFlg := false
- if len(weights) != len(h.weights) {
- nodesChgFlg = true
- } else {
- for node, newWeight := range weights {
- oldWeight, ok := h.weights[node]
- if !ok || oldWeight != newWeight {
- nodesChgFlg = true
- break
- }
- }
- }
- if nodesChgFlg {
- newhring := NewWithWeights(weights)
- h.weights = newhring.weights
- h.nodes = newhring.nodes
- h.ring = newhring.ring
- h.sortedKeys = newhring.sortedKeys
- }
- }
- func (h *HashRing) generateCircle() {
- totalWeight := 0
- for _, node := range h.nodes {
- if weight, ok := h.weights[node]; ok {
- totalWeight += weight
- } else {
- totalWeight += 1
- h.weights[node] = 1
- }
- }
- for _, node := range h.nodes {
- weight := h.weights[node]
- factor := math.Floor(float64(40*len(h.nodes)*weight) / float64(totalWeight))
- for j := 0; j < int(factor); j++ {
- nodeKey := fmt.Sprintf("%s-%d", node, j)
- bKey := hashDigest(nodeKey)
- for i := 0; i < 3; i++ {
- key := hashVal(bKey[i*4 : i*4+4])
- h.ring[key] = node
- h.sortedKeys = append(h.sortedKeys, key)
- }
- }
- }
- sort.Sort(HashKeyOrder(h.sortedKeys))
- }
- func (h *HashRing) GetNode(stringKey string) (node string, ok bool) {
- pos, ok := h.GetNodePos(stringKey)
- if !ok {
- return "", false
- }
- return h.ring[h.sortedKeys[pos]], true
- }
- func (h *HashRing) GetNodePos(stringKey string) (pos int, ok bool) {
- if len(h.ring) == 0 {
- return 0, false
- }
- key := h.GenKey(stringKey)
- nodes := h.sortedKeys
- pos = sort.Search(len(nodes), func(i int) bool { return nodes[i] > key })
- if pos == len(nodes) {
- // Wrap the search, should return first node
- return 0, true
- } else {
- return pos, true
- }
- }
- func (h *HashRing) GenKey(key string) HashKey {
- bKey := hashDigest(key)
- return hashVal(bKey[0:4])
- }
- func (h *HashRing) GetNodes(stringKey string, size int) (nodes []string, ok bool) {
- pos, ok := h.GetNodePos(stringKey)
- if !ok {
- return nil, false
- }
- if size > len(h.nodes) {
- return nil, false
- }
- returnedValues := make(map[string]bool, size)
- //mergedSortedKeys := append(h.sortedKeys[pos:], h.sortedKeys[:pos]...)
- resultSlice := make([]string, 0, size)
- for i := pos; i < pos+len(h.sortedKeys); i++ {
- key := h.sortedKeys[i%len(h.sortedKeys)]
- val := h.ring[key]
- if !returnedValues[val] {
- returnedValues[val] = true
- resultSlice = append(resultSlice, val)
- }
- if len(returnedValues) == size {
- break
- }
- }
- return resultSlice, len(resultSlice) == size
- }
- func (h *HashRing) AddNode(node string) *HashRing {
- return h.AddWeightedNode(node, 1)
- }
- func (h *HashRing) AddWeightedNode(node string, weight int) *HashRing {
- if weight <= 0 {
- return h
- }
- if _, ok := h.weights[node]; ok {
- return h
- }
- nodes := make([]string, len(h.nodes), len(h.nodes)+1)
- copy(nodes, h.nodes)
- nodes = append(nodes, node)
- weights := make(map[string]int)
- for eNode, eWeight := range h.weights {
- weights[eNode] = eWeight
- }
- weights[node] = weight
- hashRing := &HashRing{
- ring: make(map[HashKey]string),
- sortedKeys: make([]HashKey, 0),
- nodes: nodes,
- weights: weights,
- }
- hashRing.generateCircle()
- return hashRing
- }
- func (h *HashRing) UpdateWeightedNode(node string, weight int) *HashRing {
- if weight <= 0 {
- return h
- }
- /* node is not need to update for node is not existed or weight is not changed */
- if oldWeight, ok := h.weights[node]; (!ok) || (ok && oldWeight == weight) {
- return h
- }
- nodes := make([]string, len(h.nodes), len(h.nodes))
- copy(nodes, h.nodes)
- weights := make(map[string]int)
- for eNode, eWeight := range h.weights {
- weights[eNode] = eWeight
- }
- weights[node] = weight
- hashRing := &HashRing{
- ring: make(map[HashKey]string),
- sortedKeys: make([]HashKey, 0),
- nodes: nodes,
- weights: weights,
- }
- hashRing.generateCircle()
- return hashRing
- }
- func (h *HashRing) RemoveNode(node string) *HashRing {
- /* if node isn't exist in hashring, don't refresh hashring */
- if _, ok := h.weights[node]; !ok {
- return h
- }
- nodes := make([]string, 0)
- for _, eNode := range h.nodes {
- if eNode != node {
- nodes = append(nodes, eNode)
- }
- }
- weights := make(map[string]int)
- for eNode, eWeight := range h.weights {
- if eNode != node {
- weights[eNode] = eWeight
- }
- }
- hashRing := &HashRing{
- ring: make(map[HashKey]string),
- sortedKeys: make([]HashKey, 0),
- nodes: nodes,
- weights: weights,
- }
- hashRing.generateCircle()
- return hashRing
- }
- func hashVal(bKey []byte) HashKey {
- return ((HashKey(bKey[3]) << 24) |
- (HashKey(bKey[2]) << 16) |
- (HashKey(bKey[1]) << 8) |
- (HashKey(bKey[0])))
- }
- func hashDigest(key string) [md5.Size]byte {
- return md5.Sum([]byte(key))
- }
|