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. ruby调用Office Jet引擎压缩access数据库

    由于单位业务需要,搭建一台服务器专门用来做数据存储.因为数据流很小,遂采用access库作为DB.开始还好,但是后来发现access数据库有一问题,就是表空间会随着使用越来越大,哪怕表里没有数据.因为 ...

  2. CDH安装系统环境准备——系统版本和安装包下载地址指南

    由于Hadoop深受客户欢迎,许多公司都推出了各自版本的Hadoop,也有一些公司则围绕Hadoop开发产品.在Hadoop生态系统中,规模最大.知名度最高的公司则是Cloudera.接下来的日子里, ...

  3. Android平台的Swift—Kotlin

    WeTest 导读 Kotlin 已经出来较长一段时间了,有些同学已经对Kotlin进行了深入的学习,甚至已经运用到了自己的项目当中,但是还有较多同学可能只是听过Kotlin或简单了解过,这篇文章的目 ...

  4. spring事务探索

    spring自建事务管理模块.而且这个事务管理是一个抽象设计,可以应用到很多场合,包括普通的DataSource,jta,jms和hibernate上.  要正确使用spring的事务,首先需要了解s ...

  5. htmldom操作添加标签顺序

    <!DOCTYPE html> <html> <body> <div id="div1"> </div> <scr ...

  6. sqlplus 分析执行计划

    转载 http://xm-koma.iteye.com/blog/1048451 对于oracle9i,需要手工设置plustrace角色,步骤如下: 1.在SQL>connect sys/密码 ...

  7. 浅入javascript正则表达式的规则.

    今天在看jQuery源码的时候,到处都是正则的用法,一气之下就狠下心来.重新回顾了一下正则.下面是做的笔记.非常浅的入门. /* i:表示不区分大小写 g:表示可以全局配置 m:表示可以多行配置 */ ...

  8. Oracle删除重复行

    Oracle删除重复行 分类: ORACLE2010-12-12 17:10 423人阅读 评论(0) 收藏 举报 oracletabledeleteintegerinsert.net 查询及删除重复 ...

  9. J2EE架构师之路

    不经意的回首,工作进入第五个年头了,发现走过了从Java程序员到J2EE架构师的历程. 发现电脑上安装了各种各样的J2EE工具:JBuilder, WSAD, Eclipse, Rose, Toget ...

  10. C语言的产生

    一:C语言的产生 C语言是1972年由美国的Dennis Ritchie设计发明的,并首次在UNIX操作系统的DEC  PDP-11计算机上使用的. 它由早期的编程语言BCPL 演变而来,随着微型计算 ...