溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

什么是一致性hash

發(fā)布時間:2021-10-14 10:17:25 來源:億速云 閱讀:139 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“什么是一致性hash”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“什么是一致性hash”吧!

什么是Hash

Hash就是把任意長度的輸入,通過散列算法,變換成固定長度的輸出,該輸出就是散列值。例如Integer.hashCode(),String.hashCode() 等。就算是輸入的那內(nèi)容不一致也有可能導致輸出的hash值一致,這種情況就是hash碰撞,hash碰撞是不可避免的,設(shè)計出高效的hash算法是不容易的。

假設(shè)服務有四臺服務器,多個用戶來訪問我們的服務,以下的分析都用此假設(shè)

普通Hash的分析

當用戶來訪問我的服務我們對請求和服務數(shù)量進行簡單的運算等到hash,然后根據(jù)算出的hash分發(fā)請求到不同的服務上。
hash計算方式為 hash = 請求%serverCount
假設(shè)現(xiàn)在有四個請求分別是 請求1,請求2,請求3,請求4,現(xiàn)在對四個請求進行計算分發(fā)請求到不同非服務上, 計算結(jié)果如下圖
什么是一致性hash

普通Hash存在的問題

通過上圖我們可以看到通過計算我們把不同的請求通過計算分別分發(fā)對應的服務器了, 但是這個方式會存在一定的問題,接下來我們就要分析了。
我們假設(shè)server3 宕機,name重新計算后的請求分發(fā)如下圖
什么是一致性hash
在實際情況下發(fā)生服務宕機或者擴容的情況是很普遍的,當發(fā)生宕機或者擴容的時候,我們之前計算的所有hash都需要重新計算,在生產(chǎn)環(huán)境下我們后臺服務器很多臺,客戶端也有很多,那么影響是很?的,縮容和擴容都會存在這樣的問題,?量?戶的請求會被路由到其他的?標服務器處理,?戶在原來服務器中的會話都會丟失。

一致性Hashg概念

一致性Hash的出現(xiàn)就解決了上述的問題,在發(fā)生宕機或者和擴容的時候盡可能少的影響請求的分發(fā)。

一致性Hash的思路如下:
什么是一致性hash
首先有一條直線,直線的開始為0,結(jié)尾為2的32次方減1,這相當于一個地址,然后把直線彎曲形成一個圓環(huán)形成閉環(huán),這就是hash環(huán)。我們對服務器求hash然后把服務器放到hash環(huán)上的對應位置上,當有請求到來時,對請求進行計算,把請求放到hash環(huán)的對應位置,然后順時針獲得最近的服務器節(jié)點。
示意圖如下
什么是一致性hash
當發(fā)生服務宕機或者擴容是請求轉(zhuǎn)發(fā)也是會發(fā)生變化的,這次我用擴容示例,宕機同理
假如我們在server1和server2之間加個server4,請求轉(zhuǎn)發(fā)如下圖
什么是一致性hash
由上圖我們可以得出當發(fā)生擴容或者宕機的時候只會影響極少數(shù)一部分的用戶,最大限度上提高的體驗

當然一致性hash也可能存在一些問題的,比如如下圖所示, 服務器分布及其不合理, 大量的請求都落在同一個服務器上,對服務的壓力較大。
什么是一致性hash
針對這種情況我們可以用增加虛擬節(jié)點的方式來盡可能更合理的分發(fā)請求來,減輕對某一服務的壓力。
如下圖我們對每個節(jié)點增加兩個虛擬節(jié)點
什么是一致性hash

實現(xiàn)普通Hash和一致性Hash

普通Hash實現(xiàn)

public static void main(String[] args) {   String[] ips = new String[]{   "101.1.1.1","101.1.1.2","101.1.1.3","101.1.1.4"};int serverCount = 4;for (String ip : ips) {   System.out.println(ip + " 的請求分發(fā)到 server" + ip.hashCode()%serverCount);}System.out.println("======================宕機分割線==========================================");// 模擬宕機一個serverCount = 3;for (String ip : ips) {   System.out.println(ip + " 的請求分發(fā)到 server" + ip.hashCode()%serverCount);}}

輸出

101.1.1.1 的請求分發(fā)到 server3101.1.1.2 的請求分發(fā)到 server0101.1.1.3 的請求分發(fā)到 server1101.1.1.4 的請求分發(fā)到 server2======================宕機分割線==========================================101.1.1.1 的請求分發(fā)到 server1101.1.1.2 的請求分發(fā)到 server2101.1.1.3 的請求分發(fā)到 server0101.1.1.4 的請求分發(fā)到 server1

一致性Hash實現(xiàn)

不帶虛擬節(jié)點實現(xiàn)

public static void main(String[] args) {   String[] serverIps = new String[]{   "101.231.123.11","11.1.112.234","123.112.11.123","232.12.11.22"};// 用來存放服務器的SortedMap<Integer, String> hashServerMap = new TreeMap<>();for (String ip : serverIps) {   hashServerMap.put(Math.abs(ip.hashCode()), ip);}// 客戶端ipString[] clientIps = new String[]{   "101.23.234.33","11.1.112.2","123.112.11.12","23.121.11.22"};for (String ip : clientIps) {   // tailMap 方法返回的是大于參數(shù)的集合SortedMap<Integer, String> serverMap = hashServerMap.tailMap(Math.abs(ip.hashCode()));// 取hash環(huán)上的第一個服務器if (serverMap.isEmpty()) {   Integer firstKey = hashServerMap.firstKey();System.out.println(ip + " 的請求分發(fā)到 " + hashServerMap.get(firstKey));}else {   // 獲取結(jié)果集的第一個服務器System.out.println(ip + " 的請求分發(fā)到 " + hashServerMap.get(serverMap.firstKey()));}}}

分發(fā)結(jié)果

101.23.234.33 的請求分發(fā)到 232.12.11.2211.1.112.2 的請求分發(fā)到 123.112.11.123123.112.11.12 的請求分發(fā)到 11.1.112.23423.121.11.22 的請求分發(fā)到 123.112.11.123

帶虛擬節(jié)點實現(xiàn)

public static void main(String[] args) {   String[] serverIps = new String[]{   "101.231.123.11","11.1.112.234"};// 每個節(jié)點的虛擬節(jié)點數(shù)int virtualNodeCount = 2;// 用來存放服務器的SortedMap<Integer, String> hashServerMap = new TreeMap<>();for (String ip : serverIps) {   hashServerMap.put(Math.abs(ip.hashCode()), ip);// 處理虛擬節(jié)點for (int i = 0; i < virtualNodeCount; i++) {   hashServerMap.put(Math.abs((ip + "#" + i).hashCode()), ip + "#" + i);}}// 客戶端ipString[] clientIps = new String[]{   "101.23.234.33","11.1.112.2","123.112.11.12","23.121.11.22"};for (String ip : clientIps) {   // tailMap 方法返回的是大于參數(shù)的集合SortedMap<Integer, String> serverMap = hashServerMap.tailMap(Math.abs(ip.hashCode()));// 取hash環(huán)上的第一個服務器if (serverMap.isEmpty()) {   Integer firstKey = hashServerMap.firstKey();System.out.println(ip + " 的請求分發(fā)到 " + hashServerMap.get(firstKey));}else {   // 獲取結(jié)果集的第一個服務器System.out.println(ip + " 的請求分發(fā)到 " + hashServerMap.get(serverMap.firstKey()));}}}

分發(fā)結(jié)果

101.23.234.33 的請求分發(fā)到 101.231.123.1111.1.112.2 的請求分發(fā)到 11.1.112.234123.112.11.12 的請求分發(fā)到 11.1.112.23423.121.11.22 的請求分發(fā)到 101.231.123.11#0

到此,相信大家對“什么是一致性hash”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI