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. ubuntu下如何安装codeblocks集成开发环境

    codeblocks是一个十分优秀的C/C++开发IDE,虽然后起之秀codelite目前来看大有超越之势哦. 不过在ubuntu下安装codeblocks却比较麻烦,不像其他linux发行版,比如s ...

  2. C# 设置Word文档背景(纯色/渐变/图片背景)

    Word是我们日常生活.学习和工作中必不可少的文档处理工具.精致美观的文档能给人带来阅读时视觉上的美感.在本篇文章中,将介绍如何使用组件Free Spire.Doc for .NET(社区版)给Wor ...

  3. C# 将PDF转为SVG的3种情况

    PDF格式的文档广泛用于各种办公场所,在工作中难免会有将PDF文档转换为其他文档格式的需要.在本篇文档中,将介绍PDF转为SVG的方法.根据不同的转换需求,这里分三种情况进行讲述,即转PDF所有页为S ...

  4. Mysql创建索引

    1.索引作用 在索引列上,除了上面提到的有序查找之外,数据库利用各种各样的快速定位技术,能够大大提高查询效率.特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍. 例如,有 ...

  5. 我对Spring的理解。

    1.Spring实现了工厂模式的工厂类(在这里有必要解释清楚什么是工厂模式),这个类名为BeanFactory(实际上是一个接口),在程序中通常BeanFactory的子类ApplicationCon ...

  6. 我的sql数据库存储过程分页- -

    以前用到数据库存储过程分页的时候都是用 not in 但是最近工作的时候,随着数据库记录的不断增大,发现not in的效率 真的不行 虽然都设置了索引,但是当记录达到10w的时候就发现不行了,都是需要 ...

  7. Storm 常用命令

    1.启动Nimbus bin/storm nimbus & 2.启动Supervisor bin/storm supervisor & 3.启动UI bin/storm ui & ...

  8. sublime使用package control安装插件

    sublime本身可以集成Package Control来进行插件安装,非常方便. 1. 安装package control(插件包管理) 1.1 进入https://packagecontrol.i ...

  9. mongodb查询语句

    左边是mongodb语句,右边是sql语句 db.users.find() select * from users db.users.find({"age" : 27}) sele ...

  10. 关于.net 保存 decimal类型数据到SQLServer2012数据库时自动取整的问题

    公司同事问我有没有遇到过decimal类型数据入库时,会自动取整的问题(比如12.3入库后值是12,12.8入库后值是13,入库后自动四舍五入自动取整): 之前就遇到过从数据去decimal类型数据时 ...