Loading

Golang一致性hash代码

Golang一致性hash代码

服务代码

package common

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

//声明新的切片类型
type units []uint32

//返回切片长度
func (x units) Len() int {
	return len(x)
}

//比对两个数大小
func (x units) Less(i, j int) bool {
	return x[i] < x[j]
}

//切片中两个值的交换
func (x units) Swap(i, j int) {
	x[i], x[j] = x[j], x[i]
}

//当hash环中没有数据时,提示错误
var errEmpty = errors.New("hash 环没有数据")

//创建结构体保存一致性hash信息
type Consistent struct {
	//hash环,key为哈希值,值存放节点的信息
	circle map[uint32]string
	//已经排序的节点hash切片
	sortedHashes units
	//虚拟节点个数,用来增加hash的平衡性
	VirtualNode int
	//map 读写锁
	sync.RWMutex
}

//创建一致性hash算法结构体,设置默认节点数量
func NewConsistent() *Consistent {
	return &Consistent{
		//初始化变量
		circle: make(map[uint32]string),
		//设置虚拟节点的个数
		VirtualNode: 20,
	}
}

//自动生成Key
func (c *Consistent) generateKey(element string, index int) string {
	//副本Key生成逻辑
	return element + strconv.Itoa(index) //将整型转换为10进制的字符串
}

//获取hash的位置
func (c *Consistent) hashKey(key string) uint32 {
	if len(key) < 64 {
		//声明一个数组,长度为64
		var srcatch [64]byte
		//赋值到数组中
		copy(srcatch[:], key)
		//使用IEEE 多项式返回数据的CRC-32校验和
		return crc32.ChecksumIEEE(srcatch[:len(key)])
	}
	//使用IEEE 多项式返回数据的CRC-32校验和
	return crc32.ChecksumIEEE([]byte(key))
}

//更新排序,方便查找
func (c *Consistent) updateSortedHashes() {
	hashes := c.sortedHashes[:0]
	//判断切片容量,是否过大,如果过大则重置
	if cap(c.sortedHashes)/(c.VirtualNode*4) > len(c.circle) {
		hashes = nil
	}

	//添加hashes
	for k := range c.circle {
		hashes = append(hashes, k)
	}

	//对所有节点hash值进行排序,
	//方便之后进行二分查找
	sort.Sort(hashes)
	//重新赋值
	c.sortedHashes = hashes

}

//向hash环中添加节点
func (c *Consistent) add(element string) {
	//循环虚拟节点,设置副本
	for i := 0; i < c.VirtualNode; i++ {
		//根据生成的虚拟节点添加到hash环中
		c.circle[c.hashKey(c.generateKey(element, i))] = element
	}

	//更新顺序
	c.updateSortedHashes()
}

//向hash环添加节点
func (c *Consistent) Add(element string) {
	c.Lock()
	defer c.Unlock()
	c.add(element)
}

//删除节点
func (c *Consistent) remove(element string) {
	for i := 0; i < c.VirtualNode; i++ {
		delete(c.circle, c.hashKey(c.generateKey(element, i)))
	}
	c.updateSortedHashes()
}

//删除一个节点
func (c *Consistent) Remove(element string) {
	c.Lock()
	defer c.Unlock()
	c.remove(element)
}

//顺时针查找最近的节点
func (c *Consistent) search(key uint32) int {
	//查找算法
	f := func(x int) bool {
		return c.sortedHashes[x] > key
	}

	//使用"二分查找"算法来搜索指定切片满足条件的最小值
	//search使用二分法进行查找
	i := sort.Search(len(c.sortedHashes), f)
	//如果超出了范围,则设置i=0
	if i >= len(c.sortedHashes) {
		i = 0
	}
	return i
}

//根据数据标示获取最近的服务器节点信息
func (c *Consistent) Get(name string) (string, error) {
	//添加锁
	c.RLock()
	//解锁
	defer c.RUnlock()
	//解锁
	//如果为0则返回错误
	if len(c.circle) == 0 {
		return "", errEmpty
	}
	//计算hash值
	key := c.hashKey(name)
	i := c.search(key)
	return c.circle[c.sortedHashes[i]], nil
}

调用代码

var localhost = ""

func main() {

	//负载均衡器设置,采用一致性哈希算法
	hashConsistent = common.NewConsistent()
	//采用一致性哈希算法,添加节点到哈希环中
	for _, v := range hostArr {
		log.Println("-----" + v)
		hashConsistent.Add(v)
	}

	//获取本机IP
	localIP, err := GetIntranceIp()
	if err != nil {
		log.Println("获取本机IP地址失败")
	}
	localhost = localIP
	log.Println("本机IP为:" + localIP)
    
}

//获取本机IP
func GetIntranceIp() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, address := range addrs {
		//检查Ip地址判断是否回环地址
		if ipNet, ok := address.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
			if ipNet.IP.To4() != nil {
				return ipNet.IP.String(), nil
			}
		}
	}

	return "", errors.New("获取地址异常")
}

//获取分布式分配
func (a *AccessControl) GetDistributedRight(r *http.Request) bool {
	//获取用户cookie中的UID
	uid, errCookie := r.Cookie("uid")
	if errCookie != nil {
		return false
	}

	//采用一致性hash算法,根据用户ID,判断获取所属的机器
	hostRequest, errGet := hashConsistent.Get(uid.Value)
	if errGet != nil {
		return false
	}
	//判断是否为本机
	if hostRequest == localhost {
		//是本机,执行本机数据的读取和校验
		return a.GetDataFromMap(uid.Value)
	} else {
		//不是本机,充当代理访问数据的返回结果
		return GetDataFromOtherMap(hostRequest, r)
	}
}
	
posted @ 2021-07-30 15:26  我爱吃炒鸡  阅读(63)  评论(0编辑  收藏  举报