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. SQLSERVER 性能优化之Perfmon指标

    Perfmon是Windows系统性能监视程序.用于监视CPU使用率.内存使用率.硬盘读写速度.网络速度等. Processor/%Privileged Time阀值:如果数值持续大于75%就表示存在 ...

  2. SpringBoot2.0之一 新建项目helloWorld

    SpringBoot 以简单快速很快获得了广大开发者的青睐,本套SpringBoot系列以最新的SpringBoot 2.0为基础,同时会提及不同版本下SpringBoot的修改和变化,如有理解不当的 ...

  3. JAVA 综合面试题

    JAVA 综合面试题 2007-08-12 目录 TOC \o "1-3" \h \z \u Java面试题整理 9 Java面向对象 9 1. super()与this()的区别 ...

  4. OOP的基本原则

    OOP的基本原则 点击打开链接

  5. iOS Swift开发的一些坑

    0.人难招,特别是对于没钱的小团队,大多数的iOS开发者没有Swift经验,有经验的也并不是很深入 0.1.语言unwrap坑,虽然有自动修正提示,但感觉代码过程不流畅. 1.Realm的缺憾: 最近 ...

  6. Android两级导航菜单栏的实现--FragmentTabHost结合ViewPager与Android 开源项目PagerSlidingTabStrip

    http://www.cnblogs.com/aademeng/articles/6119737.html 转载注:简单总结一下,外层Tab用TabHost,类层Tab用Viepager+Framen ...

  7. 2013-9 OWASP论坛

    Broken We Application Project   ------这个PPT需要下载 OWASP BWA----- A Virtual machine---收集 Broken Web App ...

  8. 测试驱动开发 TDD

    一.详解TDD 1.1.TDD概念 :Test Drived Develop 测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种方法论.TDD的原理是在开发功能代码之前,编写单元测试用例代码,测试 ...

  9. [Micropython]TPYBoard v10x NRF24L01无线通讯模块使用教程

    1.实验目的: •       学习使用NRF24L01无线通讯模块 2.所需原器件: •       TPYBoard v10X开发板两块 •       NRF24L01无线通讯模块两个 •    ...

  10. 前端应该了解的PWA

    一.传统web 应用 当前web应用在移动时代并没有达到其在桌面设备上流行的程度,下面有张图来对比与原生应用之间的差别. 究其原因,无外乎下面不可避免的几点: 移动设备网络限制-不可忽略的加载时间 w ...