import (
    "errors"
    "hash/crc32"
    "sort"
    "strconv"
    "sync"
)

type uints []uint32   //实现 sort接口

// Len returns the length of the uints array.
func (x uints) Len() int { return len(x) }

// Less returns true if element i is less than element j.
func (x uints) Less(i, j int) bool { return x[i] < x[j] }

// Swap exchanges elements i and j.
func (x uints) Swap(i, j int) { x[i], x[j] = x[j], x[i] }

// ErrEmptyCircle is the error returned when trying to get an element when nothing has been added to hash.
var ErrEmptyCircle = errors.New("empty circle")

// Consistent holds the information about the members of the consistent hash circle.
//Consistent 数据结构
type Consistent struct {
    circle           map[uint32]string
    members          map[string]bool
    sortedHashes     uints
    NumberOfReplicas int
    count            int64
    scratch          [64]byte
    sync.RWMutex
}

// New creates a new Consistent object with a default setting of 20 replicas for each entry.
//
// To change the number of replicas, set NumberOfReplicas before adding entries.
func New() *Consistent {
    c := new(Consistent)
    c.NumberOfReplicas = 20
    c.circle = make(map[uint32]string)
    c.members = make(map[string]bool)
    return c
}

// eltKey generates a string key for an element with an index.
func (c *Consistent) eltKey(elt string, idx int) string {
    // return elt + "|" + strconv.Itoa(idx)
    return strconv.Itoa(idx) + elt
}

// Add inserts a string element in the consistent hash.
func (c *Consistent) Add(elt string) {
    c.Lock()
    defer c.Unlock()
    c.add(elt)
}

// need c.Lock() before calling
func (c *Consistent) add(elt string) {
    for i := 0; i < c.NumberOfReplicas; i++ {
        c.circle[c.hashKey(c.eltKey(elt, i))] = elt
    }
    c.members[elt] = true
    c.updateSortedHashes()
    c.count++
}

// Remove removes an element from the hash.
func (c *Consistent) Remove(elt string) {
    c.Lock()
    defer c.Unlock()
    c.remove(elt)
}

// need c.Lock() before calling
func (c *Consistent) remove(elt string) {
    for i := 0; i < c.NumberOfReplicas; i++ {
        delete(c.circle, c.hashKey(c.eltKey(elt, i)))
    }
    delete(c.members, elt)
    c.updateSortedHashes()
    c.count--
}

// Set sets all the elements in the hash.  If there are existing elements not
// present in elts, they will be removed.
func (c *Consistent) Set(elts []string) {
    c.Lock()
    defer c.Unlock()
    for k := range c.members {
        found := false
        for _, v := range elts {
            if k == v {
                found = true
                break
            }
        }
        if !found {
            c.remove(k)
        }
    }
    for _, v := range elts {
        _, exists := c.members[v]
        if exists {
            continue
        }
        c.add(v)
    }
}

func (c *Consistent) Members() []string {
    c.RLock()
    defer c.RUnlock()
    var m []string
    for k := range c.members {
        m = append(m, k)
    }
    return m
}

// Get returns an element close to where name hashes to in the circle.
func (c *Consistent) Get(name string) (string, error) {
    c.RLock()
    defer c.RUnlock()
    if len(c.circle) == 0 {
        return "", ErrEmptyCircle
    }
    key := c.hashKey(name)
    i := c.search(key)
    return c.circle[c.sortedHashes[i]], nil
}

func (c *Consistent) search(key uint32) (i int) {
    f := func(x int) bool {
        return c.sortedHashes[x] > key
    }
    i = sort.Search(len(c.sortedHashes), f)
    if i >= len(c.sortedHashes) {
        i = 0
    }
    return
}

// GetTwo returns the two closest distinct elements to the name input in the circle.
func (c *Consistent) GetTwo(name string) (string, string, error) {
    c.RLock()
    defer c.RUnlock()
    if len(c.circle) == 0 {
        return "", "", ErrEmptyCircle
    }
    key := c.hashKey(name)
    i := c.search(key)
    a := c.circle[c.sortedHashes[i]]

    if c.count == 1 {
        return a, "", nil
    }

    start := i
    var b string
    for i = start + 1; i != start; i++ {
        if i >= len(c.sortedHashes) {
            i = 0
        }
        b = c.circle[c.sortedHashes[i]]
        if b != a {
            break
        }
    }
    return a, b, nil
}

// GetN returns the N closest distinct elements to the name input in the circle.
func (c *Consistent) GetN(name string, n int) ([]string, error) {
    c.RLock()
    defer c.RUnlock()

    if len(c.circle) == 0 {
        return nil, ErrEmptyCircle
    }

    if c.count < int64(n) {
        n = int(c.count)
    }

    var (
        key   = c.hashKey(name)
        i     = c.search(key)
        start = i
        res   = make([]string, 0, n)
        elem  = c.circle[c.sortedHashes[i]]
    )

    res = append(res, elem)

    if len(res) == n {
        return res, nil
    }

    for i = start + 1; i != start; i++ {
        if i >= len(c.sortedHashes) {
            i = 0
        }
        elem = c.circle[c.sortedHashes[i]]
        if !sliceContainsMember(res, elem) {
            res = append(res, elem)
        }
        if len(res) == n {
            break
        }
    }

    return res, nil
}

func (c *Consistent) hashKey(key string) uint32 {
    if len(key) < 64 {
        var scratch [64]byte
        copy(scratch[:], key)
        return crc32.ChecksumIEEE(scratch[:len(key)])
    }
    return crc32.ChecksumIEEE([]byte(key))
}

func (c *Consistent) updateSortedHashes() {
    hashes := c.sortedHashes[:0]
    //reallocate if we're holding on to too much (1/4th)
    if cap(c.sortedHashes)/(c.NumberOfReplicas*4) > len(c.circle) {
        hashes = nil
    }
    for k := range c.circle {
        hashes = append(hashes, k)
    }
    sort.Sort(hashes)
    c.sortedHashes = hashes
}

func sliceContainsMember(set []string, member string) bool {
    for _, m := range set {
        if m == member {
            return true
        }
    }
    return false
}

consistent.go 源码阅读的更多相关文章

  1. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  2. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

  3. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

  4. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  5. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  6. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  7. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  8. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

  9. 【原】AFNetworking源码阅读(一)

    [原]AFNetworking源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 AFNetworking版本:3.0.4 由于我平常并没有经常使用AFNetw ...

随机推荐

  1. C# / VB.NET合并PDF指定页

    在前面的文章中,我们已经知道如何合并.拆分多个PDF文件,在这篇文章中的合并.拆分PDF文档主要是以方便文档管理的目的来操作文档,在文档查阅.管理及存储上很方便实用.但是我们如果想要合并多个文档中的部 ...

  2. 收藏 - android

    收藏 - android开发 2018-05-04 16:39:36 介绍:这篇文章是收藏系列的开山第一篇,主要收藏了跟android开发有关的一些内容,也算是内容汇总,后期会持续更新: 内容目录 1 ...

  3. 如何在不使用三大地图的KEY和相关组件的情况下,直接传参数到相关的H5地图

    以高德地图为例: window.location.href='http://m.amap.com/navigation/index/daddr=104.188206%2C30.858513%2C'+' ...

  4. ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?

    官方已经给出解决方案:https://github.com/pyenv/pyenv/wiki/Common-build-problems#error-the-python-ssl-extension- ...

  5. SpringMVC配置多个数据源

    多数据源,说白了,就是多数据库. 想要实现多数据库查询,只需简单四步即可实现! 第一步: 配置 jdbc.properties: # MySQL #========================== ...

  6. 4sumii

    problem description: there is four number list named A,B,C,D; now you should out put the num of  tup ...

  7. 基于elk 实现nginx日志收集与数据分析。

    一.背景 前端web服务器为nginx,采用filebeat + logstash + elasticsearch + granfa 进行数据采集与展示,对客户端ip进行地域统计,监控服务器响应时间等 ...

  8. Python_文本操作

    #向文本文件中写入内容 s='Hello world\n文本文件的读取方法\n文本文件的写入方法\n' f=open('sample1.txt','a+') #打开文件 f.write(s) #写入文 ...

  9. Java 使用BigDecimal类处理高精度计算

    Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的 ...

  10. ubuntu18.04安装搜狗拼音

    1.下载好搜狗安装包http://pinyin.sogou.com/linux/ ,注意位数! 2.打开终端安装依赖    sudo apt install libfcitx-qt0 3.提取下载好的 ...