衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Dubbo負載均衡策略之一致性哈希

OSC開源社區 ? 來源:京東技術 ? 2023-06-16 15:30 ? 次閱讀

導讀

本文主要講解了一致性哈希算法的原理以及其存在的數據傾斜的問題,然后引出解決數據傾斜問題的方法,最后分析一致性哈希算法在Dubbo中的使用。通過這篇文章,可以了解到一致性哈希算法的原理以及這種算法存在的問題和解決方案。

01負載均衡

在這里引用dubbo官網的一段話——

LoadBalance 中文意思為負載均衡,它的職責是將網絡請求,或者其他形式的負載“均攤”到不同的機器上。避免集群中部分服務器壓力過大,而另一些服務器比較空閑的情況。通過負載均衡,可以讓每臺服務器獲取到適合自己處理能力的負載。在為高負載服務器分流的同時,還可以避免資源浪費,一舉兩得。負載均衡可分為軟件負載均衡和硬件負載均衡。在我們日常開發中,一般很難接觸到硬件負載均衡。但軟件負載均衡還是可以接觸到的,比如 Nginx。在 Dubbo 中,也有負載均衡的概念和相應的實現。Dubbo 需要對服務消費者的調用請求進行分配,避免少數服務提供者負載過大。服務提供者負載過大,會導致部分請求超時。因此將負載均衡到每個服務提供者上,是非常必要的。Dubbo 提供了4種負載均衡實現,分別是基于權重隨機算法的 RandomLoadBalance、基于最少活躍調用數算法的 LeastActiveLoadBalance、基于hash 一致性的 ConsistentHashLoadBalance,以及基于加權輪詢算法的 RoundRobinLoadBalance。這幾個負載均衡算法代碼不是很長,但是想看懂也不是很容易,需要對這幾個算法的原理有一定了解才行。

02 哈希算法

7d315652-0c16-11ee-962d-dac502259ad0.png

圖1無哈希算法請求

如上所示,假設0,1,2號服務器都存儲的有用戶信息,那么當我們需要獲取某用戶信息時,因為我們不知道該用戶信息存放在哪一臺服務器中,所以需要分別查詢0,1,2號服務器。這樣獲取數據的效率是極低的。

對于這樣的場景,我們可以引入哈希算法。

7d532782-0c16-11ee-962d-dac502259ad0.png

圖2引入哈希算法后的請求

還是上面的場景,但前提是每一臺服務器存放用戶信息時是根據某一種哈希算法存放的。所以取用戶信息的時候,也按照同樣的哈希算法取即可。

假設我們要查詢用戶號為100的用戶信息,經過某個哈希算法,比如這里的userId mod n,即100 mod 3結果為1。所以用戶號100的這個請求最終會被1號服務器接收并處理。

這樣就解決了無效查詢的問題。

但是這樣的方案會帶來什么問題呢?

擴容或者縮容時,會導致大量的數據遷移。最少也會影響50%的數據。

7d6e16a0-0c16-11ee-962d-dac502259ad0.png

圖3增加一臺服務器

為了說明問題,加入一臺服務器3。服務器的數量n就從3變成了4。還是查詢用戶號為100的用戶信息時,100 mod 4結果為0。這時,請求就被0號服務器接收了。

當服務器數量為3時,用戶號為100的請求會被1號服務器處理。

當服務器數量為4時,用戶號為100的請求會被0號服務器處理。

所以,當服務器數量增加或者減少時,一定會涉及到大量數據遷移的問題。

對于上述哈希算法其優點是簡單易用,大多數分庫分表規則就采取的這種方式。一般是提前根據數據量,預先估算好分區數。

其缺點是由于擴容或收縮節點導致節點數量變化時,節點的映射關系需要重新計算,會導致數據進行遷移。所以擴容時通常采用翻倍擴容,避免數據映射全部被打亂,導致全量遷移的情況,這樣只會發生50%的數據遷移。

03一致性哈希算法

一致性 hash 算法由麻省理工學院的 Karger 及其合作者于1997年提出的,算法提出之初是用于大規模緩存系統的負載均衡。它的工作過程是這樣的,首先根據 ip 或者其他的信息為緩存節點生成一個 hash,并將這個 hash 投射到 [0, 232 - 1] 的圓環上。當有查詢或寫入請求時,則為緩存項的 key 生成一個 hash 值。然后查找第一個大于或等于該 hash 值的緩存節點,并到這個節點中查詢或寫入緩存項。如果當前節點掛了,則在下一次查詢或寫入緩存時,為緩存項查找另一個大于其 hash 值的緩存節點即可。大致效果如下圖所示,每個緩存節點在圓環上占據一個位置。如果緩存項的 key 的 hash 值小于緩存節點 hash 值,則到該緩存節點中存儲或讀取緩存項。比如下面綠色點對應的緩存項將會被存儲到 cache-2 節點中。由于 cache-3 掛了,原本應該存到該節點中的緩存項最終會存儲到cache-4節點中。

7daecbaa-0c16-11ee-962d-dac502259ad0.png

圖4一致性哈希算法

在一致性哈希算法中,不管是增加節點,還是宕機節點,受影響的區間僅僅是增加或者宕機服務器在哈希環空間中,逆時針方向遇到的第一臺服務器之間的區間,其它區間不會受到影響。

但是一致性哈希也是存在問題的:

7dda212e-0c16-11ee-962d-dac502259ad0.png

圖5數據傾斜

當節點很少的時候可能會出現這樣的分布情況,A服務會承擔大部分請求。這種情況就叫做數據傾斜。

那如何解決數據傾斜的問題呢?

加入虛擬節點。

首先一個服務器根據需要可以有多個虛擬節點。假設一臺服務器有n個虛擬節點。那么哈希計算時,可以使用IP+端口+編號的形式進行哈希值計算。其中的編號就是0到n的數字。由于IP+端口是一樣的,所以這n個節點都是指向的同一臺機器。

7e009e30-0c16-11ee-962d-dac502259ad0.png

圖6引入虛擬節點

在沒有加入虛擬節點之前,A服務器承擔了絕大多數的請求。但是假設每個服務器有一個虛擬節點(A-1,B-1,C-1),經過哈希計算后落在了如上圖所示的位置。那么A服務器的承擔的請求就在一定程度上(圖中標注了五角星的部分)分攤給了B-1、C-1虛擬節點,實際上就是分攤給了B、C服務器。

一致性哈希算法中,加入虛擬節點,可以解決數據傾斜問題。

04一致性哈希在DUBBO中的應用

7e38a140-0c16-11ee-962d-dac502259ad0.png

圖7Dubbo中一致性哈希環

這里相同顏色的節點均屬于同一個服務提供者,比如 Invoker1-1,Invoker1-2,……, Invoker1-160。這樣做的目的是通過引入虛擬節點,讓 Invoker 在圓環上分散開來,避免數據傾斜問題。所謂數據傾斜是指,由于節點不夠分散,導致大量請求落到了同一個節點上,而其他節點只會接收到了少量請求的情況。比如:

7e6dc262-0c16-11ee-962d-dac502259ad0.png

圖8數據傾斜問題

如上,由于 Invoker-1 和 Invoker-2 在圓環上分布不均,導致系統中75%的請求都會落到 Invoker-1 上,只有 25% 的請求會落到 Invoker-2 上。解決這個問題辦法是引入虛擬節點,通過虛擬節點均衡各個節點的請求量。

到這里背景知識就普及完了,接下來開始分析源碼。我們先從 ConsistentHashLoadBalance 的 doSelect 方法開始看起,如下:

public class ConsistentHashLoadBalance extends AbstractLoadBalance {


    private final ConcurrentMap> selectors = 
        new ConcurrentHashMap>();


    @Override
    protected  Invoker doSelect(List> invokers, URL url, Invocation invocation) {
        String methodName = RpcUtils.getMethodName(invocation);
        String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;


        // 獲取 invokers 原始的 hashcode
        int identityHashCode = System.identityHashCode(invokers);
        ConsistentHashSelector selector = (ConsistentHashSelector) selectors.get(key);
        // 如果 invokers 是一個新的 List 對象,意味著服務提供者數量發生了變化,可能新增也可能減少了。
        // 此時 selector.identityHashCode != identityHashCode 條件成立
        if (selector == null || selector.identityHashCode != identityHashCode) {
            // 創建新的 ConsistentHashSelector
            selectors.put(key, new ConsistentHashSelector(invokers, methodName, identityHashCode));
            selector = (ConsistentHashSelector) selectors.get(key);
        }


        // 調用 ConsistentHashSelector 的 select 方法選擇 Invoker
        return selector.select(invocation);
    }
    
    private static final class ConsistentHashSelector {...}
}

如上,doSelect 方法主要做了一些前置工作,比如檢測 invokers 列表是不是變動過,以及創建 ConsistentHashSelector。這些工作做完后,接下來開始調用 ConsistentHashSelector 的 select 方法執行負載均衡邏輯。在分析 select 方法之前,我們先來看一下一致性 hash 選擇器 ConsistentHashSelector 的初始化過程,如下:

private static final class ConsistentHashSelector {


    // 使用 TreeMap 存儲 Invoker 虛擬節點
    private final TreeMap> virtualInvokers;


    private final int replicaNumber;


    private final int identityHashCode;


    private final int[] argumentIndex;


    ConsistentHashSelector(List> invokers, String methodName, int identityHashCode) {
        this.virtualInvokers = new TreeMap>();
        this.identityHashCode = identityHashCode;
        URL url = invokers.get(0).getUrl();
        // 獲取虛擬節點數,默認為160
        this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
        // 獲取參與 hash 計算的參數下標值,默認對第一個參數進行 hash 運算
        String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
        argumentIndex = new int[index.length];
        for (int i = 0; i < index.length; i++) {
            argumentIndex[i] = Integer.parseInt(index[i]);
        }
        for (Invoker invoker : invokers) {
            String address = invoker.getUrl().getAddress();
            for (int i = 0; i < replicaNumber / 4; i++) {
                // 對 address + i 進行 md5 運算,得到一個長度為16的字節數組
                byte[] digest = md5(address + i);
                // 對 digest 部分字節進行4次 hash 運算,得到四個不同的 long 型正整數
                for (int h = 0; h < 4; h++) {
                    // h = 0 時,取 digest 中下標為 0 ~ 3 的4個字節進行位運算
                    // h = 1 時,取 digest 中下標為 4 ~ 7 的4個字節進行位運算
                    // h = 2, h = 3 時過程同上
                    long m = hash(digest, h);
                    // 將 hash 到 invoker 的映射關系存儲到 virtualInvokers 中,
                    // virtualInvokers 需要提供高效的查詢操作,因此選用 TreeMap 作為存儲結構
                    virtualInvokers.put(m, invoker);
                }
            }
        }
    }
}

ConsistentHashSelector 的構造方法執行了一系列的初始化邏輯,比如從配置中獲取虛擬節點數以及參與 hash 計算的參數下標,默認情況下只使用第一個參數進行 hash。需要特別說明的是,ConsistentHashLoadBalance 的負載均衡邏輯只受參數值影響,具有相同參數值的請求將會被分配給同一個服務提供者。ConsistentHashLoadBalance 不 關系權重,因此使用時需要注意一下。

在獲取虛擬節點數和參數下標配置后,接下來要做的事情是計算虛擬節點 hash 值,并將虛擬節點存儲到 TreeMap 中。到此,ConsistentHashSelector 初始化工作就完成了。接下來,我們來看看 select 方法的邏輯。

public Invoker select(Invocation invocation) {
    // 將參數轉為 key
    String key = toKey(invocation.getArguments());
    // 對參數 key 進行 md5 運算
    byte[] digest = md5(key);
    // 取 digest 數組的前四個字節進行 hash 運算,再將 hash 值傳給 selectForKey 方法,
    // 尋找合適的 Invoker
    return selectForKey(hash(digest, 0));
}


private Invoker selectForKey(long hash) {
    // 到 TreeMap 中查找第一個節點值大于或等于當前 hash 的 Invoker
    Map.Entry> entry = virtualInvokers.tailMap(hash, true).firstEntry();
    // 如果 hash 大于 Invoker 在圓環上最大的位置,此時 entry = null,
    // 需要將 TreeMap 的頭節點賦值給 entry
    if (entry == null) {
        entry = virtualInvokers.firstEntry();
    }


    // 返回 Invoker
    return entry.getValue();
}

如上,選擇的過程相對比較簡單了。首先是對參數進行 md5 以及 hash 運算,得到一個 hash 值。然后再拿這個值到 TreeMap 中查找目標 Invoker 即可。

到此關于 ConsistentHashLoadBalance 就分析完了。

在閱讀ConsistentHashLoadBalance 源碼之前,建議讀者先補充背景知識,不然看懂代碼邏輯會有很大難度。

05應用場景

DNS負載均衡最早的負載均衡技術是通過DNS來實現的,在DNS中為多個地址配置同一個名字,因而查詢這個名字的客戶機將得到其中一個地址,從而使得不同的客戶訪問不同的服務器,達到負載均衡的目的。DNS負載均衡是一種簡單而有效的方法,但是它不能區分服務器的差異,也不能反映服務器的當前運行狀態。

代理服務器負載均衡使用代理服務器,可以將請求轉發給內部的服務器,使用這種加速模式顯然可以提升靜態網頁的訪問速度。然而,也可以考慮這樣一種技術,使用代理服務器將請求均勻轉發給多臺服務器,從而達到負載均衡的目的。

地址轉換網關負載均衡支持負載均衡的地址轉換網關,可以將一個外部IP地址映射為多個內部IP地址,對每次TCP連接請求動態使用其中一個內部地址,達到負載均衡的目的。

協議內部支持負載均衡除了這三種負載均衡方式之外,有的協議內部支持與負載均衡相關的功能,例如HTTP協議中的重定向能力等,HTTP運行于TCP連接的最高層。

NAT負載均衡NAT(Network Address Translation網絡地址轉換)簡單地說就是將一個IP地址轉換為另一個IP地址,一般用于未經注冊的內部地址與合法的、已獲注冊的Internet IP地址間進行轉換。適用于解決Internet IP地址緊張、不想讓網絡外部知道內部網絡結構等的場合下。

反向代理負載均衡普通代理方式是代理內部網絡用戶訪問internet上服務器的連接請求,客戶端必須指定代理服務器,并將本來要直接發送到internet上服務器的連接請求發送給代理服務器處理。反向代理(Reverse Proxy)方式是指以代理服務器來接受internet上的連接請求,然后將請求轉發給內部網絡上的服務器,并將從服務器上得到的結果返回給internet上請求連接的客戶端,此時代理服務器對外就表現為一個服務器。反向代理負載均衡技術是把將來自internet上的連接請求以反向代理的方式動態地轉發給內部網絡上的多臺服務器進行處理,從而達到負載均衡的目的。

混合型負載均衡在有些大型網絡,由于多個服務器群內硬件設備、各自的規模、提供的服務等的差異,可以考慮給每個服務器群采用最合適的負載均衡方式,然后又在這多個服務器群間再一次負載均衡或群集起來以一個整體向外界提供服務(即把這多個服務器群當做一個新的服務器群),從而達到最佳的性能。將這種方式稱之為混合型負載均衡。此種方式有時也用于單臺均衡設備的性能不能滿足大量連接請求的情況下。


審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 算法
    +關注

    關注

    23

    文章

    4630

    瀏覽量

    93352
  • 服務器
    +關注

    關注

    12

    文章

    9303

    瀏覽量

    86061
  • 負載均衡
    +關注

    關注

    0

    文章

    113

    瀏覽量

    12391
  • Dubbo
    +關注

    關注

    0

    文章

    20

    瀏覽量

    3194

原文標題:Dubbo負載均衡策略之 一致性哈希

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    高速串行總線的物理層一致性測試是什么?由來呢?

    物理層的一致性測試作為近 10 多年來示波器最主要的用途之一直是產業界最常提到的名詞之一。本文嘗試將物理層一致性測試的含義,要素與目的及
    發表于 08-12 07:17

    MIPI一致性測試

    MIPI一致性測試測試項目:> TX測試;> RX測試;> S參數和阻抗測試;> DigRF,Unipro和LLI的測試;測試環境: MIPI測試對示波器帶寬的要求 >
    發表于 09-26 13:31

    什么是霍爾元件的一致性

    什么是霍爾元件的一致性?霍爾開關元件主要是通過感應磁性來進行開關機,霍爾元件本身又屬于無觸點開關,因此具有感應距離。霍爾開關都有個觸發值和釋放值,觸發值是指霍爾元件表面達到參數磁性大小,霍爾元器件
    發表于 10-12 09:34

    順序一致性和TSO一致性分別是什么?SC和TSO到底哪個好?

    內存一致性之順序一致性(sequential consistency)可以說,最直觀的內存一致性模型是sequentially consistent(SC):內存訪問執行的順序與程序指定的順序相同
    發表于 07-19 14:54

    一致性規劃研究

    針對一致性規劃的高度求解復雜度,分析主流一致性規劃器的求解策略,給出影響一致性規劃器性能的主要因素:啟發信息的有效,信念狀態表示方法的緊湊
    發表于 04-06 08:43 ?12次下載

    CMP中Cache一致性協議的驗證

    CMP是處理器體系結構發展的個重要方向,其中Cache一致性問題的驗證是CMP設計中的項重要課題。基于MESI一致性協議,本文建立了CMP的Cache
    發表于 07-20 14:18 ?38次下載

    加速器一致性接口

    Zynq PS上的加速器一致性接口(Accelerator Coherency Port, ACP)是個兼容AXI3的64位從機接口,連接到SCU(Snoop Control Unit),為PL
    發表于 11-17 15:04 ?3751次閱讀

    分布式一致性算法Yac

    )。算法動態生成參與一致性表決的成員子集及Leader節點并時分遷移,形成統計負載均衡;去除要求全體多數派成員參與表決的強約束,使算法具備更高的失效容忍性;并通過日志鏈機制重新建立算法安全
    發表于 11-27 17:49 ?0次下載
    分布式<b class='flag-5'>一致性</b>算法Yac

    DSA系統的全局一致性需求分析

    ,分析了DSA系統的全局一致性需求;最后,重點給出了模型域、服務域和認知域內的一致性控制策略與建議。本研究可為DSA系統設計與開發人員提供技術與方法指導。
    發表于 12-06 15:28 ?0次下載
    DSA系統的全局<b class='flag-5'>一致性</b>需求分析

    Cache一致性協議優化研究

    現代晶體管技術在單芯片上集成多個處理器已經成為現實.近年來,隨著多核處理器集成核數的不斷增加,高速緩存的一致性問題凸顯出來,已成為多核處理器的性能瓶頸之一,亟待解決.介紹了片上多核處理器一致性
    發表于 12-30 15:04 ?0次下載
    Cache<b class='flag-5'>一致性</b>協議優化研究

    一致性哈希是什么?為什么它是可擴展的分布式系統架構的個必要工具

    在本文中,我們將了解一致性哈希是什么、為什么它是可擴展的分布式系統架構中的個必要工具。
    的頭像 發表于 07-17 17:57 ?4431次閱讀

    哈希圖一致性算法已被驗證為異步拜占庭容錯

    HederaHashgraph在下代公共分類帳中擁有多樣化的治理。它最近宣布哈希圖一致性算法已被驗證為異步拜占庭容錯。這是通過使用Coq系統的計算機檢查的數學證明完成的。
    發表于 10-23 11:07 ?1877次閱讀

    如何保證緩存一致性

    “ 本文的參考文章是2022年HOT 34上Intel Rob Blakenship關于CXL緩存一致性篇介紹。”
    的頭像 發表于 10-19 17:42 ?1229次閱讀
    如何保證緩存<b class='flag-5'>一致性</b>

    DDR一致性測試的操作步驟

    DDR一致性測試的操作步驟? DDR(雙數據率)一致性測試是對DDR內存模塊進行測試以確保其性能和可靠。在進行DDR一致性測試時,需要遵循
    的頭像 發表于 02-01 16:24 ?1711次閱讀

    深入理解數據備份的關鍵原則:應用一致性與崩潰一致性的區別

    深入理解數據備份的關鍵原則:應用一致性與崩潰一致性的區別 在數字化時代,數據備份成為了企業信息安全的核心環節。但在備份過程中,兩個關鍵概念——應用一致性和崩潰一致性,常常被誤解或混淆。
    的頭像 發表于 03-11 11:29 ?1008次閱讀
    深入理解數據備份的關鍵原則:應用<b class='flag-5'>一致性</b>與崩潰<b class='flag-5'>一致性</b>的區別
    天祝| 百家乐现金网最好的系统哪里有可靠吗| 蒙阴县| 百家乐娱乐场开户注册| 百家乐官网自动下注| 大发888网页免费游戏| 免费百家乐官网缩水软件| 博彩公司| 百家乐微笑不倒| 菲律宾百家乐官网开户| 360棋牌游戏| 金宝博百家乐游戏| 百家乐官网是骗人的么| 百家乐软件代理打| 百苑百家乐官网的玩法技巧和规则 | 网上百家乐官网公| 五华县| 威尼斯人娱乐城赌博| 属蛇和属猪做生意| 百家乐官网路单下注| 威尼斯人娱乐场 五星| 百家乐官网事一箩筐的微博| 威海市| 最可信百家乐娱乐城| 做生意 风水| 百家乐官网游戏真人游戏| 大发888怎么开户| 任我赢百家乐软件中国有限公司| 百家乐官网天下第一和| 淘金盈| 扑克王百家乐的玩法技巧和规则 | 百家乐官网娱乐城官方网| 娱乐城送现金| 百家乐多少钱| 百家乐下载游戏| 澳门百家乐官网玩法与游戏规则| 巴登娱乐城真人娱乐| 宝马会百家乐娱乐城| 百家乐注册开户| 百家乐官网搏牌| 沧州市|