您好,登錄后才能下訂單哦!
本篇內容主要講解“什么是一致性hash”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“什么是一致性hash”吧!
Hash就是把任意長度的輸入,通過散列算法,變換成固定長度的輸出,該輸出就是散列值。例如Integer.hashCode(),String.hashCode() 等。就算是輸入的那內容不一致也有可能導致輸出的hash值一致,這種情況就是hash碰撞,hash碰撞是不可避免的,設計出高效的hash算法是不容易的。
假設服務有四臺服務器,多個用戶來訪問我們的服務,以下的分析都用此假設
當用戶來訪問我的服務我們對請求和服務數量進行簡單的運算等到hash,然后根據算出的hash分發請求到不同的服務上。
hash計算方式為 hash = 請求%serverCount
假設現在有四個請求分別是 請求1,請求2,請求3,請求4,現在對四個請求進行計算分發請求到不同非服務上, 計算結果如下圖
通過上圖我們可以看到通過計算我們把不同的請求通過計算分別分發對應的服務器了, 但是這個方式會存在一定的問題,接下來我們就要分析了。
我們假設server3 宕機,name重新計算后的請求分發如下圖
在實際情況下發生服務宕機或者擴容的情況是很普遍的,當發生宕機或者擴容的時候,我們之前計算的所有hash都需要重新計算,在生產環境下我們后臺服務器很多臺,客戶端也有很多,那么影響是很?的,縮容和擴容都會存在這樣的問題,?量?戶的請求會被路由到其他的?標服務器處理,?戶在原來服務器中的會話都會丟失。
一致性Hash的出現就解決了上述的問題,在發生宕機或者和擴容的時候盡可能少的影響請求的分發。
一致性Hash的思路如下:
首先有一條直線,直線的開始為0,結尾為2的32次方減1,這相當于一個地址,然后把直線彎曲形成一個圓環形成閉環,這就是hash環。我們對服務器求hash然后把服務器放到hash環上的對應位置上,當有請求到來時,對請求進行計算,把請求放到hash環的對應位置,然后順時針獲得最近的服務器節點。
示意圖如下
當發生服務宕機或者擴容是請求轉發也是會發生變化的,這次我用擴容示例,宕機同理
假如我們在server1和server2之間加個server4,請求轉發如下圖
由上圖我們可以得出當發生擴容或者宕機的時候只會影響極少數一部分的用戶,最大限度上提高的體驗
當然一致性hash也可能存在一些問題的,比如如下圖所示, 服務器分布及其不合理, 大量的請求都落在同一個服務器上,對服務的壓力較大。
針對這種情況我們可以用增加虛擬節點的方式來盡可能更合理的分發請求來,減輕對某一服務的壓力。
如下圖我們對每個節點增加兩個虛擬節點
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 + " 的請求分發到 server" + ip.hashCode()%serverCount);}System.out.println("======================宕機分割線==========================================");// 模擬宕機一個serverCount = 3;for (String ip : ips) { System.out.println(ip + " 的請求分發到 server" + ip.hashCode()%serverCount);}}
輸出
101.1.1.1 的請求分發到 server3101.1.1.2 的請求分發到 server0101.1.1.3 的請求分發到 server1101.1.1.4 的請求分發到 server2======================宕機分割線==========================================101.1.1.1 的請求分發到 server1101.1.1.2 的請求分發到 server2101.1.1.3 的請求分發到 server0101.1.1.4 的請求分發到 server1
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 方法返回的是大于參數的集合SortedMap<Integer, String> serverMap = hashServerMap.tailMap(Math.abs(ip.hashCode()));// 取hash環上的第一個服務器if (serverMap.isEmpty()) { Integer firstKey = hashServerMap.firstKey();System.out.println(ip + " 的請求分發到 " + hashServerMap.get(firstKey));}else { // 獲取結果集的第一個服務器System.out.println(ip + " 的請求分發到 " + hashServerMap.get(serverMap.firstKey()));}}}
分發結果
101.23.234.33 的請求分發到 232.12.11.2211.1.112.2 的請求分發到 123.112.11.123123.112.11.12 的請求分發到 11.1.112.23423.121.11.22 的請求分發到 123.112.11.123
public static void main(String[] args) { String[] serverIps = new String[]{ "101.231.123.11","11.1.112.234"};// 每個節點的虛擬節點數int virtualNodeCount = 2;// 用來存放服務器的SortedMap<Integer, String> hashServerMap = new TreeMap<>();for (String ip : serverIps) { hashServerMap.put(Math.abs(ip.hashCode()), ip);// 處理虛擬節點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 方法返回的是大于參數的集合SortedMap<Integer, String> serverMap = hashServerMap.tailMap(Math.abs(ip.hashCode()));// 取hash環上的第一個服務器if (serverMap.isEmpty()) { Integer firstKey = hashServerMap.firstKey();System.out.println(ip + " 的請求分發到 " + hashServerMap.get(firstKey));}else { // 獲取結果集的第一個服務器System.out.println(ip + " 的請求分發到 " + hashServerMap.get(serverMap.firstKey()));}}}
分發結果
101.23.234.33 的請求分發到 101.231.123.1111.1.112.2 的請求分發到 11.1.112.234123.112.11.12 的請求分發到 11.1.112.23423.121.11.22 的請求分發到 101.231.123.11#0
到此,相信大家對“什么是一致性hash”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。