在分布式系统开发中,我们经常会遇到服务器负载均衡的问题,我们需要将用户的请求均匀的分摊到每一台服务器,从而保证系统资源的有效利用。那么如何将请求均匀的进行分配呢?比较常见的就是 Hash 算法了,但是普通的余数hash(hash(比如用户id)%服务器机器数)算法伸缩性很差,当新增或者下线服务器机器时候,用户id与服务器的映射关系会大量失效。一致性hash则利用hash环对其进行了改进。
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
常用的 Hash 算法如下:
(1)MD4
MD4(RFC 1320)是 MIT 的Ronald L. Rivest在 1990 年设计的,MD 是 Message Digest(消息摘要) 的缩写。它适用在32位字长的处理器上用高速软件实现——它是基于 32位操作数的位操作来实现的。
(2)MD5
MD5(RFC 1321)是 Rivest 于1991年对MD4的改进版本。它对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。MD5比MD4来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。
(3)SHA-1及其他
SHA1是由NIST NSA设计为同DSA一起使用的,它对长度小于264的输入,产生长度为160bit的散列值,因此抗穷举(brute-force)性更好。SHA-1 设计时基于和MD4相同原理,并且模仿了该算法。
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 。
我们通过算法来实现一个一致性 Hash:
#mermaid-svg-6O2bayLuphFw0TN0 .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-6O2bayLuphFw0TN0 .label text{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .node rect,#mermaid-svg-6O2bayLuphFw0TN0 .node circle,#mermaid-svg-6O2bayLuphFw0TN0 .node ellipse,#mermaid-svg-6O2bayLuphFw0TN0 .node polygon,#mermaid-svg-6O2bayLuphFw0TN0 .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-6O2bayLuphFw0TN0 .node .label{text-align:center;fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .node.clickable{cursor:pointer}#mermaid-svg-6O2bayLuphFw0TN0 .arrowheadPath{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-6O2bayLuphFw0TN0 .flowchart-link{stroke:#333;fill:none}#mermaid-svg-6O2bayLuphFw0TN0 .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-6O2bayLuphFw0TN0 .edgeLabel rect{opacity:0.9}#mermaid-svg-6O2bayLuphFw0TN0 .edgeLabel span{color:#333}#mermaid-svg-6O2bayLuphFw0TN0 .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-6O2bayLuphFw0TN0 .cluster text{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-6O2bayLuphFw0TN0 .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-6O2bayLuphFw0TN0 text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-6O2bayLuphFw0TN0 .actor-line{stroke:grey}#mermaid-svg-6O2bayLuphFw0TN0 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-6O2bayLuphFw0TN0 .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-6O2bayLuphFw0TN0 #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-6O2bayLuphFw0TN0 .sequenceNumber{fill:#fff}#mermaid-svg-6O2bayLuphFw0TN0 #sequencenumber{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 #crosshead path{fill:#333;stroke:#333}#mermaid-svg-6O2bayLuphFw0TN0 .messageText{fill:#333;stroke:#333}#mermaid-svg-6O2bayLuphFw0TN0 .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-6O2bayLuphFw0TN0 .labelText,#mermaid-svg-6O2bayLuphFw0TN0 .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-6O2bayLuphFw0TN0 .loopText,#mermaid-svg-6O2bayLuphFw0TN0 .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-6O2bayLuphFw0TN0 .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-6O2bayLuphFw0TN0 .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-6O2bayLuphFw0TN0 .noteText,#mermaid-svg-6O2bayLuphFw0TN0 .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-6O2bayLuphFw0TN0 .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-6O2bayLuphFw0TN0 .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-6O2bayLuphFw0TN0 .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-6O2bayLuphFw0TN0 .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .section{stroke:none;opacity:0.2}#mermaid-svg-6O2bayLuphFw0TN0 .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-6O2bayLuphFw0TN0 .section2{fill:#fff400}#mermaid-svg-6O2bayLuphFw0TN0 .section1,#mermaid-svg-6O2bayLuphFw0TN0 .section3{fill:#fff;opacity:0.2}#mermaid-svg-6O2bayLuphFw0TN0 .sectionTitle0{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .sectionTitle1{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .sectionTitle2{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .sectionTitle3{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-6O2bayLuphFw0TN0 .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .grid path{stroke-width:0}#mermaid-svg-6O2bayLuphFw0TN0 .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-6O2bayLuphFw0TN0 .task{stroke-width:2}#mermaid-svg-6O2bayLuphFw0TN0 .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .taskText:not([font-size]){font-size:11px}#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-6O2bayLuphFw0TN0 .task.clickable{cursor:pointer}#mermaid-svg-6O2bayLuphFw0TN0 .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-6O2bayLuphFw0TN0 .taskText0,#mermaid-svg-6O2bayLuphFw0TN0 .taskText1,#mermaid-svg-6O2bayLuphFw0TN0 .taskText2,#mermaid-svg-6O2bayLuphFw0TN0 .taskText3{fill:#fff}#mermaid-svg-6O2bayLuphFw0TN0 .task0,#mermaid-svg-6O2bayLuphFw0TN0 .task1,#mermaid-svg-6O2bayLuphFw0TN0 .task2,#mermaid-svg-6O2bayLuphFw0TN0 .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutside0,#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutside2{fill:#000}#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutside1,#mermaid-svg-6O2bayLuphFw0TN0 .taskTextOutside3{fill:#000}#mermaid-svg-6O2bayLuphFw0TN0 .active0,#mermaid-svg-6O2bayLuphFw0TN0 .active1,#mermaid-svg-6O2bayLuphFw0TN0 .active2,#mermaid-svg-6O2bayLuphFw0TN0 .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-6O2bayLuphFw0TN0 .activeText0,#mermaid-svg-6O2bayLuphFw0TN0 .activeText1,#mermaid-svg-6O2bayLuphFw0TN0 .activeText2,#mermaid-svg-6O2bayLuphFw0TN0 .activeText3{fill:#000 !important}#mermaid-svg-6O2bayLuphFw0TN0 .done0,#mermaid-svg-6O2bayLuphFw0TN0 .done1,#mermaid-svg-6O2bayLuphFw0TN0 .done2,#mermaid-svg-6O2bayLuphFw0TN0 .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-6O2bayLuphFw0TN0 .doneText0,#mermaid-svg-6O2bayLuphFw0TN0 .doneText1,#mermaid-svg-6O2bayLuphFw0TN0 .doneText2,#mermaid-svg-6O2bayLuphFw0TN0 .doneText3{fill:#000 !important}#mermaid-svg-6O2bayLuphFw0TN0 .crit0,#mermaid-svg-6O2bayLuphFw0TN0 .crit1,#mermaid-svg-6O2bayLuphFw0TN0 .crit2,#mermaid-svg-6O2bayLuphFw0TN0 .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-6O2bayLuphFw0TN0 .activeCrit0,#mermaid-svg-6O2bayLuphFw0TN0 .activeCrit1,#mermaid-svg-6O2bayLuphFw0TN0 .activeCrit2,#mermaid-svg-6O2bayLuphFw0TN0 .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-6O2bayLuphFw0TN0 .doneCrit0,#mermaid-svg-6O2bayLuphFw0TN0 .doneCrit1,#mermaid-svg-6O2bayLuphFw0TN0 .doneCrit2,#mermaid-svg-6O2bayLuphFw0TN0 .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-6O2bayLuphFw0TN0 .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-6O2bayLuphFw0TN0 .milestoneText{font-style:italic}#mermaid-svg-6O2bayLuphFw0TN0 .doneCritText0,#mermaid-svg-6O2bayLuphFw0TN0 .doneCritText1,#mermaid-svg-6O2bayLuphFw0TN0 .doneCritText2,#mermaid-svg-6O2bayLuphFw0TN0 .doneCritText3{fill:#000 !important}#mermaid-svg-6O2bayLuphFw0TN0 .activeCritText0,#mermaid-svg-6O2bayLuphFw0TN0 .activeCritText1,#mermaid-svg-6O2bayLuphFw0TN0 .activeCritText2,#mermaid-svg-6O2bayLuphFw0TN0 .activeCritText3{fill:#000 !important}#mermaid-svg-6O2bayLuphFw0TN0 .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-6O2bayLuphFw0TN0 g.classGroup text .title{font-weight:bolder}#mermaid-svg-6O2bayLuphFw0TN0 g.clickable{cursor:pointer}#mermaid-svg-6O2bayLuphFw0TN0 g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-6O2bayLuphFw0TN0 g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-6O2bayLuphFw0TN0 .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-6O2bayLuphFw0TN0 .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-6O2bayLuphFw0TN0 .dashed-line{stroke-dasharray:3}#mermaid-svg-6O2bayLuphFw0TN0 #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 .commit-id,#mermaid-svg-6O2bayLuphFw0TN0 .commit-msg,#mermaid-svg-6O2bayLuphFw0TN0 .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-6O2bayLuphFw0TN0 g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-6O2bayLuphFw0TN0 g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-6O2bayLuphFw0TN0 g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-6O2bayLuphFw0TN0 .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-6O2bayLuphFw0TN0 .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-6O2bayLuphFw0TN0 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-6O2bayLuphFw0TN0 .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-6O2bayLuphFw0TN0 .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-6O2bayLuphFw0TN0 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-6O2bayLuphFw0TN0 .edgeLabel text{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-6O2bayLuphFw0TN0 .node circle.state-start{fill:black;stroke:black}#mermaid-svg-6O2bayLuphFw0TN0 .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-6O2bayLuphFw0TN0 #statediagram-barbEnd{fill:#9370db}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-state .divider{stroke:#9370db}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-6O2bayLuphFw0TN0 .note-edge{stroke-dasharray:5}#mermaid-svg-6O2bayLuphFw0TN0 .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-6O2bayLuphFw0TN0 .error-icon{fill:#522}#mermaid-svg-6O2bayLuphFw0TN0 .error-text{fill:#522;stroke:#522}#mermaid-svg-6O2bayLuphFw0TN0 .edge-thickness-normal{stroke-width:2px}#mermaid-svg-6O2bayLuphFw0TN0 .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-6O2bayLuphFw0TN0 .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-6O2bayLuphFw0TN0 .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-6O2bayLuphFw0TN0 .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-6O2bayLuphFw0TN0 .marker{fill:#333}#mermaid-svg-6O2bayLuphFw0TN0 .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-6O2bayLuphFw0TN0 { color: rgba(0, 0, 0, 0.75); font: ; } 初始化哈希环 对哈希环的key进行排序 通过hash值匹配node节点 package main import ( "fmt" "hash/crc32" "math/rand" "sort" "strconv" "sync" "time" ) // 一致性 Hash 节点副本数量 const DEFAULT_REPLICAS = 100 // SortKeys 存储一致性 Hash 的值 type SortKeys []uint32 // Len 一致性哈希数量 func (sk SortKeys) Len() int { return len(sk) } // Less Hash 值的比较 func (sk SortKeys) Less(i, j int) bool { return sk[i] < sk[j] } // Swap 交换两个 Hash 值 func (sk SortKeys) Swap(i, j int) { sk[i], sk[j] = sk[j], sk[i] } // Hash 环,存储每一个节点的信息 type HashRing struct { Nodes map[uint32]string Keys SortKeys sync.RWMutex } // New 根据node新建一个Hash环 func (hr *HashRing) New(nodes []string) { if nodes == nil { return } hr.Nodes = make(map[uint32]string) hr.Keys = SortKeys{} for _, node := range nodes { // Hash 通过 node 节点名称生成哈希值,该Hash值指向对应 node 节点 hr.Nodes[hr.Hash(str)] = node // 将哈希值保存在key列表中 hr.Keys = append(hr.Keys, hr.Hash(str)) } // 对 Hash 值进行排序,后面计算的 Hash 值与 Keys 进行比较,取大于等于计算所得的Hash值所对应的node节点 sort.Sort(hr.Keys) } // hashStr 根据Key计算Hash值 func (hr *HashRing) Hash(key string) uint32 { return crc32.ChecksumIEEE([]byte(key)) } // GetNode 根据key找出对应的node节点 func (hr *HashRing) GetNode(key string) string { hr.RLock() defer hr.RUnlock() hash := hr.Hash(key) i := hr.get_position(hash) return hr.Nodes[hr.Keys[i]] } // get_position 找出第一个大于等于 hash 的key func (hr *HashRing) get_position(hash uint32) (i int) { i = sort.Search(len(hr.Keys), func(i int) bool { return hr.Keys[i] >= hash }) if i >= len(hr.Keys) { return 0 } return } func main() { var nodes []string // 初始化 node 节点 for i := 1; i < 6; i++ { nodes = append(nodes, fmt.Sprintf("Server%d", i)) } hashR := new(HashRing) // 生成 hash 环 hashR.New(nodes) rand.Seed(time.Now().Unix()) // 寻找匹配的 node 节点 fmt.Println("random1", " 发送到 ", hashR.GetNode("random1")) }在服务器数量比较少的情况下,一致性 Hash 非常容易出现数据倾斜的问题,为了解决这个问题,人们引入了一致性 Hash 来解决这个问题。
// New 根据node新建一个Hash环 func (hr *HashRing) New(nodes []string) { if nodes == nil { return } hr.Nodes = make(map[uint32]string) hr.Keys = SortKeys{} for _, node := range nodes { // 每个节点生成 DEFAULT_REPLICAS 个虚拟节点 for i := 0; i < DEFAULT_REPLICAS; i++ { str := node + strconv.Itoa(i) // Hash 通过 node 虚拟节点名称生成哈希值,该Hash值指向对应 node 节点 hr.Nodes[hr.Hash(str)] = node // 将哈希值保存在key列表中 hr.Keys = append(hr.Keys, hr.Hash(str)) } } // 对 Hash 值进行排序,后面计算的 Hash 值与 Keys 进行比较,取大于等于计算所得的Hash值所对应的node节点 sort.Sort(hr.Keys) } // Hash 根据Key计算Hash值 func (hr *HashRing) Hash(key string) uint32 { return crc32.ChecksumIEEE([]byte(key)) }