一文看懂一致性Hash插值
2025-10-31 12:16:48
同时 3 台多线程伺服器,分别为 CS1、CS2 和 CS3:
则可知,各具本体来话说和伺服器的射影人关系如下:
K1 => CS1K4 => CS3K2 => CS2K3 => CS1
即:
以上便是精确病态Hash的工作化学现象;
可以看不到,精确病态Hash就是:将原本单个点的Hash射影,转变为了在一个环状上的某个视频上的射影!
解释器我们来看几种伺服器扩缩容的情节;
伺服器扩缩容情节 ① 伺服器减低
论据 CS3 伺服器于是又次出现短路随之而来服务下线,这时原本打印于 CS3 伺服器的具本体来话说 o4,所需被重新调配至 CS2 伺服器,其它具本体来话说仍打印在原有的机器人上:
此时不受不良影响的多达据只有 CS2 和 CS3 伺服器之间的其余部分多达据!
② 伺服器减低
假如客运量锐减,我们所需减低机器人伺服器 CS4,经过同样的 hash 运算,该伺服器最终吊于 t1 和 t2 伺服器之间,确切如下绘出附注:
此时,只有 t1 和 t2 伺服器之间的其余部分具本体来话说所需重新调配;
在以上解释器中的只有 o3 具本体来话说所需重新调配,即它被重新到 CS4 伺服器;
在中间我们早就话说过:如果有别于简便的取模方法,当新附加伺服器时可能才会随之而来大其余部分多线程失效,而有别于精确病态校验启发式后,这种上述情况得到了较不算的改善,因为只有不算其余部分具本体来话说所需重新调配!
多达据偏斜Wild伺服器效能平衡上述情况 便是上述情况
在右边证明了的举例来话说中的,各个伺服器几乎是千分之被均摊到Hash环状上;
但是在仅仅情节中的不用选取到一个Hash字符串这么理想的将各个伺服器散列到Hash环状上;
此时,在伺服器键值多达量太不算的之前提,很非常容易因为键值分布不不规则而引发多达据倾斜上述情况;
如下绘出被多线程的具本体来话说大其余部分多线程在node-4伺服器上,随之而来其他键值资源浪费,子系统受压大其余部分集中的在 node-4键值上,这样的炮兵部队是非常不心理健康的:
同时,还有另一个上述情况:
在右边新增伺服器 CS4 时,CS4 只分担了 CS1 伺服器的损耗,伺服器 CS2 和 CS3 并没有因为 CS4 伺服器的加入而减低损耗受压;如果 CS4 伺服器的效能与原有伺服器的效能一致甚至可能更高,那么这种结果并不是我们所努力的;
ID键值
针对右边的上述情况,我们可以通过:替换出ID键值来解决上述情况损耗不适度的上述情况:
即将每台化学伺服器ID为除此以外ID伺服器,将ID伺服器放到到校验环状上,如果要确定具本体来话说的伺服器,需先确定具本体来话说的ID伺服器,于是又由ID伺服器确定化学伺服器;
如下绘出附注:
在绘出中的:o1 和 o2 透露具本体来话说,v1 ~ v6 透露ID伺服器,s1 ~ s3 透露仅仅的化学伺服器;
ID键值的测算
ID键值的hash测算通常可以有别于:也就是话说键值的IP位址加多达字E冠词 hash(10.24.23.227#1) 的方式;
举个举例来话说,node-1键值IP为10.24.23.227,正常测算node-1的hash值:
hash(10.24.23.227#1)% 2And32论据我们给node-1设置三个ID键值,node-1#1、 node-1#2、 node-1#3,对它们来进行hash后取模:
hash(10.24.23.227#1)% 2And32 hash(10.24.23.227#2)% 2And32 hash(10.24.23.227#3)% 2And32注意:
调配的ID键值整多达越多多,射影在hash环状上才才会越多趋近不规则,键值太不算的话不用看出功效;
替换出ID键值的同时也减低了新的上述情况,要做ID键值和确实键值间的射影,具本体来话说key->ID键值->仅仅键值之间的转换;
有别于情节精确病态hash在分布式子系统中的不该是做到损耗适度的首选启发式,它的做到比较灵活,既可以在会话做到,也可以在中的间件上做到,比如日常有别于相当多的多线程中的间件memcached和 redis炮兵部队都有比如话说它;
memcached的炮兵部队比较特殊,严格来话说它不用算是伪炮兵部队,因为它的伺服器之间不用收发,劝话说的分发IP只不过靠会话来的测算出多线程具本体来话说不该吊在哪个伺服器上,而它的IP启发式用的就是精确病态hash;
还有redis炮兵部队中的hash槽的观念,虽然做到不尽相同,但意识形态万变不离其宗,看完本篇的精确病态hash,你于是又去理解redis槽位就轻松多了;
其它的应用情节还有很多:
RPC基础 Dubbo用来选择服务提供者 分布式人关系多达据库分库分此表:多达据与键值的射影人关系 LVS损耗适度调度器 …… 精确病态Hash启发式做到解释器我们根据右边的话说什么述,有别于Golang做到一个精确病态Hash启发式,这个启发式带有一些解释器的功能物理病态质:
精确病态Hash框架启发式; 支持自观念Hash启发式; 支持自观念ID键值整多达;确切源解释器见:
解释器开始做到吧!
内部结构本体、差错以及也就是说观念 ① 内部结构本体观念
首先观念每机器人多线程伺服器的多线程:
core/host.go
type Host struct { // the host id: ip:port Name string // the load bound of the host LoadBound int64}
其中的:
Name:多线程伺服器的Ip位址 + 终端,如:127.0.0.1:8000 LoadBound:多线程伺服器当之前执行的“劝话说”多线程多达,这个codice_在应是内含损耗边境值的精确病态Hash中的才会比如话说;其次,观念精确病态Hash的内部结构:
core/algorithm.go
// Consistent is an implementation of consistent-hashing-algorithmtype Consistent struct { // the number of replicas replicaNum int // the total loads of all replicas totalLoad int64 // the hash function for keys hashFunc func(key string) uint64 // the map of virtual nodes to hosts hostMap map[string]*Host // the map of hashed virtual nodes to host name replicaHostMap map[uint64]string // the hash ring sortedHostsHashSet []uint64 // the hash ring lock sync.RWMutex}
其中的:
replicaNum:透露每个确实的多线程伺服器在Hash环状中的存有的ID键值多达; totalLoad:所有化学伺服器也就是话说的总多线程“劝话说”多达(这个codice_在应是内含损耗边境值的精确病态Hash中的才会比如话说); hashFunc:测算Hash环状射影以及Key射影的散列字符串; hostMap:化学伺服器地名也就是话说的Host内部结构本体射影; replicaHostMap:Hash环状中的ID键值也就是话说确实多线程伺服器地名的射影; sortedHostsHashSet:Hash环状; sync.RWMutex:操纵Hash环状时比如话说的手写针;约莫的内部结构如上附注,解释器我们来看一些也就是说和差错的观念;
② 也就是说和差错观念
也就是说的观念如下:
core/algorithm.go
const ( // The format of the host replica name hostReplicaFormat = 人口为120人%s%d人口为120人)var ( // the default number of replicas defaultReplicaNum = 10 // the load bound factor // ref: loadBoundFactor = 0.25 // the default Hash function for keys defaultHashFunc = func(key string) uint64 { out := sha512.Sum512([]byte(key)) return binary.LittleEndian.Uint64(out[:]) })
分别透露:
defaultReplicaNum:当之前之前提,每个确实的化学伺服器在Hash环状中的ID键值的整多达; loadBoundFactor:损耗边境因多达(这个codice_在应是内含损耗边境值的精确病态Hash中的才会比如话说); defaultHashFunc:当之前的散列字符串,这里比如话说的是SHA512启发式,并取的是unsigned int64,这一点和右边引介的 0~2And32-1大不相同! hostReplicaFormat:ID键值地名文件格式,这里的ID键值的文件格式为:%s%d,和上文谈到的 10.24.23.227#1的文件格式大不相同,但是道理是一样的!还有一些差错的观念:
core/error.go
var ( ErrHostAlreadyExists = errors.New(Wildquothost already exists") ErrHostNotFound = errors.New(Wildquothost not found"))
分别透露伺服器早就申领,以及多线程伺服器未回来到;
解释器来看确切的方法做到!
申领/持有人多线程伺服器 ① 申领多线程伺服器
申领多线程伺服器的解释器如下:
core/algorithm.go
func (c *Consistent) RegisterHost(hostName string) error { c.Lock defer c.Unlock if _, ok := c.hostMap[hostName] ok { return ErrHostAlreadyExists } c.hostMap[hostName] = WildampHost{ Name: hostName, LoadBound: 0, } for i := 0 i
解释器比较简便,简便话说一下;
首先,检查和伺服器否早就申领,如果早就申领,则直接赶回早就申领的差错;
随后,始创一个Host具本体来话说,并且在 for 重复状中的始创多个ID键值:
根据 hashFunc 测算伺服器散列值【注:此处测算的散列值可能和之之前的值存有武装冲突,本做到中的暂不慎重考虑这种情节】; 将散列值加入 replicaHostMap 中的; 将散列值加入 sortedHostsHashSet 中的;于是又一,对Hash环状来进行先后顺序;
这里有别于字符串作为Hash环状只是为了便于话说明,在仅仅做到中的提议换用其他多线程来进行做到,以换取更好的效能;
当多线程伺服器信息写入 replicaHostMap 射影以及 Hash 环状后,即顺利完出了多线程伺服器的申领;
② 持有人多线程伺服器
持有人多线程伺服器的解释器如下:
core/algorithm.go
func (c *Consistent) UnregisterHost(hostName string) error { c.Lock defer c.Unlock if _, ok := c.hostMap[hostName] !ok { return ErrHostNotFound } delete(c.hostMap, hostName) for i := 0 i val { r = m - 1 } } if idx != -1 { c.sortedHostsHashSet = append(c.sortedHostsHashSet[:idx], c.sortedHostsHashSet[idx+1:]...) }}
和申领多线程伺服器相反,将伺服器在 Map 射影以及 Hash 环状中的去除即顺利完出了持有人;
这里的逻辑上和右边申领的逻辑上极为类似,这里不于是又赘述!
转发Key(框架)
转发 Key 是整个精确病态 Hash 启发式的框架,但是做到起来也并不多样;
解释器如下:
core/algorithm.go
func (c *Consistent) GetKey(key string) (string, error) { hashedKey := c.hashFunc(key) idx := c.searchKey(hashedKey) return c.replicaHostMap[c.sortedHostsHashSet[idx]], nil}func (c *Consistent) searchKey(key uint64) int { idx := sort.Search(len(c.sortedHostsHashSet), func(i int) bool { return c.sortedHostsHashSet[i]>= key }) if idx>= len(c.sortedHostsHashSet) { // make search as a ring idx = 0 } return idx}
解释器首先测算 key 的散列值;
随后,在Hash环状上“由东向西”回来到可以多线程的第机器人多线程伺服器:
idx := sort.Search(len(c.sortedHostsHashSet), func(i int) bool { return c.sortedHostsHashSet[i]>= key})
注意到,如果 key 比当之前Hash环状中的最小的ID键值的 hash 值还大,则选择当之前 Hash环状 中的 hash 值最小者的一个键值(即“环状形”的逻辑上):
if idx>= len(c.sortedHostsHashSet) { // make search as a ring idx = 0}
searchKey 赶回了ID键值在 Hash 环状字符串中的的 index;
随后,我们有别于 map 赶回 index 也就是话说的多线程伺服器的地名只需;
至此,精确病态 Hash 启发式整体做到,于是又一我们来检验一下;
精确病态Hash启发式实践与化验 启发式检验之前将要 ① 多线程伺服器将要
在检验启发式之之前,我们还所需将要几台多线程伺服器;
为了简便起见,这里有别于了 HTTP 伺服器作为多线程伺服器,确切解释器如下附注:
server/main.go
package mainimport ( Wildquotflag" Wildquotfmt" Wildquotnet/http" Wildquotsync" Wildquottime")type CachedMap struct { KvMap sync.Map Lock sync.RWMutex}var ( cache = CachedMap{KvMap: sync.Map{}} port = flag.String(Wildquotp", Wildquot8080", Wildquotport") regHost = Wildquot" expireTime = 10)func main { flag.Parse stopChan := make(chan interface{}) startServer(*port) <-stopChan}func startServer(port string) { hostName := fmt.Sprintf(Wildquotlocalhost:%s", port) fmt.Printf(Wildquotstart server: %s", port) err := registerHost(hostName) if err != nil { panic(err) } http.HandleFunc("/", kvHandle) err = http.ListenAndServe(":"+port, nil) if err != nil { err = unregisterHost(hostName) if err != nil { panic(err) } panic(err) }}func kvHandle(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm if _, ok := cache.KvMap.Load(r.Form[Wildquotkey"][0]) !ok { val := fmt.Sprintf(Wildquothello: %s", r.Form[Wildquotkey"][0]) cache.KvMap.Store(r.Form[Wildquotkey"][0], val) fmt.Printf(Wildquotcached key: {%s: %s}", r.Form[Wildquotkey"][0], val) time.AfterFunc(time.Duration(expireTime)*time.Second, func { cache.KvMap.Delete(r.Form[Wildquotkey"][0]) fmt.Printf(Wildquotremoved cached key after 3s: {%s: %s}", r.Form[Wildquotkey"][0], val) }) } val, _ := cache.KvMap.Load(r.Form[Wildquotkey"][0]) _, err := fmt.Fprintf(w, val.(string)) if err != nil { panic(err) }}func registerHost(host string) error { resp, err := http.Get(fmt.Sprintf("%s/register?host=%s", regHost, host)) if err != nil { return err } defer resp.Body.Close return nil}func unregisterHost(host string) error { resp, err := http.Get(fmt.Sprintf("%s/unregister?host=%s", regHost, host)) if err != nil { return err } defer resp.Body.Close return nil}
解释器接不受由GUI指定的 -p值指定伺服器终端号;
解释器执行后,才会子程序 startServer字符串启动时一个http伺服器;
在 startServer字符串中的,首先子程序 registerHost在代理伺服器上来进行申领(上文才会话说什么),并窃听 /路径,确切解释器如下:
func startServer(port string) { hostName := fmt.Sprintf(Wildquotlocalhost:%s", port) fmt.Printf(Wildquotstart server: %s", port) err := registerHost(hostName) if err != nil { panic(err) } http.HandleFunc("/", kvHandle) err = http.ListenAndServe(":"+port, nil) if err != nil { err = unregisterHost(hostName) if err != nil { panic(err) } panic(err) }}
kvHandle字符串对劝话说来进行执行:
func kvHandle(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm if _, ok := cache.KvMap.Load(r.Form[Wildquotkey"][0]) !ok { val := fmt.Sprintf(Wildquothello: %s", r.Form[Wildquotkey"][0]) cache.KvMap.Store(r.Form[Wildquotkey"][0], val) fmt.Printf(Wildquotcached key: {%s: %s}", r.Form[Wildquotkey"][0], val) time.AfterFunc(time.Duration(expireTime)*time.Second, func { cache.KvMap.Delete(r.Form[Wildquotkey"][0]) fmt.Printf(Wildquotremoved cached key after 3s: {%s: %s}", r.Form[Wildquotkey"][0], val) }) } val, _ := cache.KvMap.Load(r.Form[Wildquotkey"][0]) _, err := fmt.Fprintf(w, val.(string)) if err != nil { panic(err) }}
首先,判别来自路径的值:?key=xxx;
随后,转发伺服器中的的多线程(为了简便起见,这里有别于 sync.Map来建模多线程):
如果多线程不存有,则写入多线程,并通过 time.AfterFunc设置多线程到期一段时间( expireTime);于是又一,赶回多线程;
② 多线程代理伺服器将要
有了多线程伺服器最后,我们还所需一个代理伺服器来选择确切选择哪个多线程伺服器来劝话说;
解释器如下:
proxy/proxy.go
package proxyimport ( Wildquotfmt" Wildquotgithub.com/jasonkayzk/consistent-hashing-demo/core" Wildquotio/ioutil" Wildquotnet/http" Wildquottime")type Proxy struct { consistent *core.Consistent}// NewProxy creates a new Proxyfunc NewProxy(consistent *core.Consistent) *Proxy { proxy := WildampProxy{ consistent: consistent, } return proxy}func (p *Proxy) GetKey(key string) (string, error) { host, err := p.consistent.GetKey(key) if err != nil { return "", err } resp, err := http.Get(fmt.Sprintf(Wildquot%s?key=%s", host, key)) if err != nil { return "", err } defer resp.Body.Close body, _ := ioutil.ReadAll(resp.Body) fmt.Printf(WildquotResponse from host %s: %s", host, string(body)) return string(body), nil}func (p *Proxy) RegisterHost(host string) error { err := p.consistent.RegisterHost(host) if err != nil { return err } fmt.Println(fmt.Sprintf(Wildquotregister host: %s success", host)) return nil}func (p *Proxy) UnregisterHost(host string) error { err := p.consistent.UnregisterHost(host) if err != nil { return err } fmt.Println(fmt.Sprintf(Wildquotunregister host: %s success", host)) return nil}
代理伺服器的逻辑上很简便,就是始创一个精确病态Hash内部结构: Consistent,把 Consistent和劝话说多线程伺服器的逻辑上来进行了一层封装;
启发式检验 启动时代理伺服器
启动时代理伺服器的解释器如下:
package mainimport ( Wildquotfmt" Wildquotgithub.com/jasonkayzk/consistent-hashing-demo/core" Wildquotgithub.com/jasonkayzk/consistent-hashing-demo/proxy" Wildquotnet/http")var ( port = Wildquot18888" p = proxy.NewProxy(core.NewConsistent(10, nil)))func main { stopChan := make(chan interface{}) startServer(port) <-stopChan}func startServer(port string) { http.HandleFunc("/register", registerHost) http.HandleFunc("/unregister", unregisterHost) http.HandleFunc("/key", getKey) fmt.Printf(Wildquotstart proxy server: %s", port) err := http.ListenAndServe(":"+port, nil) if err != nil { panic(err) }}func registerHost(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm err := p.RegisterHost(r.Form[Wildquothost"][0]) if err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintf(w, err.Error) return } _, _ = fmt.Fprintf(w, fmt.Sprintf(Wildquotregister host: %s success", r.Form[Wildquothost"][0]))}func unregisterHost(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm err := p.UnregisterHost(r.Form[Wildquothost"][0]) if err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintf(w, err.Error) return } _, _ = fmt.Fprintf(w, fmt.Sprintf(Wildquotunregister host: %s success", r.Form[Wildquothost"][0]))}func getKey(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm val, err := p.GetKey(r.Form[Wildquotkey"][0]) if err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintf(w, err.Error) return } _, _ = fmt.Fprintf(w, fmt.Sprintf(Wildquotkey: %s, val: %s", r.Form[Wildquotkey"][0], val))}
和多线程伺服器类似,这里有别于 HTTP 伺服器来建模;
代理伺服器窃听 18888 终端的几个IP:
/register:申领多线程伺服器; /unregister:持有人多线程伺服器; /key:转发多线程Key;这里为了简便起见,有别于了这种方式来进行服务申领,仅仅有别于时请有别于其他配件来进行做到!
于是又一启动时多线程伺服器:
start proxy server: 18888 启动时多线程伺服器
分别启动时三个多线程伺服器:
$ go run server/main.go -p 8080start server: 8080$ go run server/main.go -p 8081start server: 8081$ go run server/main.go -p 8082start server: 8082
同时,代理伺服器输出:
register host: localhost:8080 successregister host: localhost:8081 successregister host: localhost:8082 success
可以看不到多线程伺服器早就出功申领;
劝话说代理伺服器换取Key
可以有别于 curl指令劝话说代理伺服器换取多线程 key:
$ curl localhost:18888/key?key=123key: 123, val: hello: 123
此时,代理伺服器输出:
Response from host localhost:8080: hello: 123
同时,8000终端的多线程伺服器输出:
cached key: {123: hello: 123}removed cached key after 10s: {123: hello: 123}
可以看不到,8000终端的伺服器对key值来进行了多线程,并在10秒后清洗了多线程;
想法多次换取Key
想法换取多个Key:
Response from host localhost:8082: hello: 45363456Response from host localhost:8080: hello: 4Response from host localhost:8082: hello: 1Response from host localhost:8080: hello: 2Response from host localhost:8082: hello: 3Response from host localhost:8080: hello: 4Response from host localhost:8082: hello: 5Response from host localhost:8080: hello: 6Response from host localhost:8082: hello: sdkbnfoerwtnbreResponse from host localhost:8082: hello: sd45555254tg423i5gvj4v5Response from host localhost:8081: hello: 0Response from host localhost:8082: hello: 032452345
可以看不到有所不同的key被散列到了有所不同的多线程伺服器;
于是又一我们通过debug详细信息确切的字符串来溜走究竟;
通过Debug详细信息申领和Hash环状
开启debug,并申领单个多线程伺服器后,详细信息 Consistent 中的的值:
申领三个多线程伺服器后,详细信息 Consistent 中的的值:
从debug中的的字符串,我们就可以很吻合的看不到申领有所不同多达量的伺服器时,精确病态Hash上伺服器的动态变化!
以上就是整体的精确病态Hash启发式的做到了!
但是很多时候,我们的多线程伺服器所需同时执行大量的多线程劝话说,而通过右边的启发式,我们总是才会去同机器人多线程伺服器去换取多线程多达据;
如果很多的热点多达据都吊在了同机器人多线程伺服器上,则可能才会于是又次出现效能难题;
Google 在2017年驳斥了: 内含损耗边境值的精确病态Hash启发式;
解释器我们在整体的精确病态Hash启发式的新的,做到内含损耗边境值的精确病态Hash!
内含损耗边境值的精确病态Hash 启发式话说明了
17年时,Google 驳斥了内含损耗边境值的精确病态Hash启发式,此启发式主要应用领域在做到精确病态的同时,做到损耗的千分之病态;
此启发式刚开始由 Vimeo 的 Andrew Rodland 在 haproxy 中的做到并开放源码;
参看:
arvix学术著作位址:
这个启发式将多线程伺服器视之为一个内含一定用量的桶(可以简便理解为Hash桶),将会话视之为任意球,则千分之病态要能透露为:所有约等于千分之反射率(任意球的多达量除以桶的多达量):
仅仅有别于时,可以设定一个千分之反射率的值 %u3B5,将每个桶的用量设置为千分之加载一段时间的 下上限 (1+%u3B5);
确切的测算过程如下:
首先,测算 key 的 Hash 值; 随后,沿着 Hash 环状由东向西回来到第机器人不受受限(千分之用量受限)的伺服器; 换取多线程;例如解释器的绘出:
有别于校验字符串将 6 个任意球和 3 个桶调配给 Hash环状 上的随机前面,论据每个桶的用量设置为 2,按 ID 值的依此类推顺序调配任意球;
1号任意球由东向西移动,转入C桶; 2号任意球转入A桶; 3号和4号任意球转入B桶; 5号任意球转入C桶; 然后6号任意球由东向西移动,首先击中的B桶;但是桶 B 的用量为 2,并且早就包含任意球 3 和 4,所以任意球 6 最后移动驶向桶 C,但该桶也已剩;于是又一,任意球 6 最终转入带有备用插座的桶 A; 启发式做到在右边整体精确病态 Hash 启发式做到的新的,我们最后做到内含损耗边境值的精确病态Hash启发式;
在框架启发式中的附加根据损耗上述情况转发Key的字符串,以及减低/释放损耗值的字符串;
根据损耗上述情况转发 Key 的字符串:
core/algorithm.go
func (c *Consistent) GetKeyLeast(key string) (string, error) { c.RLock defer c.RUnlock if len(c.replicaHostMap) == 0 { return "", ErrHostNotFound } hashedKey := c.hashFunc(key) idx := c.searchKey(hashedKey) // Find the first host that may serve the key i := idx for { host := c.replicaHostMap[c.sortedHostsHashSet[i]] loadChecked, err := c.checkLoadCapacity(host) if err != nil { return "", err } if loadChecked { return host, nil } i++ // if idx goes to the end of the ring, start from the beginning if i>= len(c.replicaHostMap) { i = 0 } }}func (c *Consistent) checkLoadCapacity(host string) (bool, error) { // a safety check if someone performed c.Done more than needed if c.totalLoad
在 GetKeyLeast 字符串中的,首先根据 searchKey 字符串,由东向西换取可能不受受限的第一个ID键值;
随后子程序 checkLoadCapacity 校验当之前多线程伺服器的损耗多达否不受受限:
candidateHost.LoadBound+1 <= (c.totalLoad + 1) / len(hosts) * (1 + loadBoundFactor)如果不不受受限,则沿着 Hash 环状走到下一个ID键值,最后判别否不受受限,直到不受受限;
这里有别于的是无条件的 for重复状,因为一定存有略低于 千分之损耗 (1 + loadBoundFactor) 的ID键值!*
减低/释放损耗值的字符串:
core/algorithm.go
func (c *Consistent) Inc(hostName string) { c.Lock defer c.Unlock atomic.AddInt64(Wildampc.hostMap[hostName].LoadBound, 1) atomic.AddInt64(Wildampc.totalLoad, 1)}func (c *Consistent) Done(host string) { c.Lock defer c.Unlock if _, ok := c.hostMap[host] !ok { return } atomic.AddInt64(Wildampc.hostMap[host].LoadBound, -1) atomic.AddInt64(Wildampc.totalLoad, -1)}
逻辑上比较简便,就是原子的对也就是话说多线程伺服器来进行损耗加减一操纵;
启发式检验 改写代理伺服器解释器
在代理伺服器中的减低IP:
proxy/proxy.go
func (p *Proxy) GetKeyLeast(key string) (string, error) { host, err := p.consistent.GetKeyLeast(key) if err != nil { return "", err } p.consistent.Inc(host) time.AfterFunc(time.Second*10, func { // drop the host after 10 seconds(for testing)! fmt.Printf(Wildquotdropping host: %s after 10 second", host) p.consistent.Done(host) }) resp, err := http.Get(fmt.Sprintf(Wildquot%s?key=%s", host, key)) if err != nil { return "", err } defer resp.Body.Close body, _ := ioutil.ReadAll(resp.Body) fmt.Printf(WildquotResponse from host %s: %s", host, string(body)) return string(body), nil}
注意:这里建模的是单个key劝话说可能才会过后10s大钟;
启动时代理伺服器时减低IP:
main.go
func startServer(port string) { // ...... http.HandleFunc("/key_least", getKeyLeast) // ......}func getKeyLeast(w http.ResponseWriter, r *http.Request) { _ = r.ParseForm val, err := p.GetKeyLeast(r.Form[Wildquotkey"][0]) if err != nil { w.WriteHeader(http.StatusInternalServerError) _, _ = fmt.Fprintf(w, err.Error) return } _, _ = fmt.Fprintf(w, fmt.Sprintf(Wildquotkey: %s, val: %s", r.Form[Wildquotkey"][0], val))} 检验
启动时代理伺服器,并开启第二台多线程伺服器;
通过解释器的指令换取内含损耗边境的Key:
$ curl localhost:18888/key_least?key=123key: 123, val: hello: 123
多次劝话说后的结果如下:
人口为120人人口为120人人口为120人start proxy server: 18888register host: localhost:8080 successregister host: localhost:8081 successregister host: localhost:8082 successResponse from host localhost:8080: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8081: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8081: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8081: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8081: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8080: hello: 123Response from host localhost:8082: hello: 123Response from host localhost:8082: hello: 123人口为120人人口为120人人口为120人
可以看不到,多线程被均摊到了其他伺服器(这是由于一个多线程劝话说才会过后10s随之而来的)!
概述
本文抛砖引玉的话说什么解了精确病态Hash启发式的化学现象,并提供了Go的做到;
在此基础正中央,根据 Google 的学术著作做到了带有损耗边境的精确病态Hash启发式;
当然右边的解释器在仅仅生产环状境下即便如此所需其余部分简化,如:
服务申领; 多线程伺服器做到; 心跳探测; ……大家在仅仅有别于时,可以根据所需,混搭仅仅的配件!
。杭州看男科的专业医院杭州看妇科哪个医院比较好
郑州看男科哪家最好
21岁男咳嗽吐黄痰怎么办
多感染几次就习惯了?当心,新冠感染次数越多,危险系数越高!
血糖正常值是多少
健康医药资讯
佝偻病

-
搞笑GIF趣图:我问老婆究竟为什么要这样欺骗我
搞怪GIF趣图:我问老婆究竟为什么要这样骗我 1、鸣出差忘了,偷偷的问哥哥:“哥哥,妈妈出差期间,小孩和其他爱人有约但会了没人有?”哥哥:“有,王叔叔经常有约小孩独自出去
2025-10-31 00:16:48

-
搞笑GIF趣图:我还没有开始发招都从未被治着了
幽默GIF趣图:我还从未开始发招都之前被治着了 1、老公看我这几天不收挺累的,就关怀的问道道:“媳妇,你打算吃什么,我去给你做!”我打算了打算时说道:“我打算喝醉大骨头鸡
2025-10-31 00:16:48

-
搞笑GIF趣图:我个头,我老公更个头,公公婆婆也个头
搞笑GIF趣图:我小孩子,我老公更是小孩子,公公老奶奶也小孩子 1、我傻是欧几里得班上,而出名梯形图形!家里也有很多梯形图形的摆设,比如东西墙各有一幅画,南北边各有一盆绿
2025-10-31 00:16:48

-
搞笑GIF趣图:我家乡有这么一个人,抽烟十多年了
吐槽GIF趣图:我全家人有这么一个人,抽屑十多年了 1、那时候比较调皮,一次在学校打了一老师,起初不敢跟家里感叹是,于是帮陪老师借钱财给老师看病,一同班女老师还给我最多,
2025-10-31 00:16:48

-
搞笑GIF趣图:我老婆的小姐妹随便都称我为姐夫
搞笑GIF趣图:我老婆的小姐妹平时都称我为妹夫 1、直到现在傍晚,在公交站台等车的时候,一漂亮傻接二连三把袋子打后下,里都是杨梅。傻说道道:来,大家都吃到点。我见旁边的人
2025-10-31 00:16:48