国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

這篇具有很好參考價值的文章主要介紹了Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

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

一、負載均衡

在這里引用dubbo官網(wǎng)的一段話——

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

二、哈希算法

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖1 無哈希算法請求

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

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

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

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

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

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

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

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

擴容或者縮容時,會導(dǎo)致大量的數(shù)據(jù)遷移。最少也會影響50%的數(shù)據(jù)。

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖3 增加一臺服務(wù)器

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

  • 當服務(wù)器數(shù)量為3時,用戶號為100的請求會被1號服務(wù)器處理。
  • 當服務(wù)器數(shù)量為4時,用戶號為100的請求會被0號服務(wù)器處理。

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

對于上述哈希算法其優(yōu)點是簡單易用,大多數(shù)分庫分表規(guī)則就采取的這種方式。一般是提前根據(jù)數(shù)據(jù)量,預(yù)先估算好分區(qū)數(shù)。

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

三、一致性哈希算法

**一致性 hash 算法由麻省理工學(xué)院的 Karger 及其合作者于1997年提出的,算法提出之初是用于大規(guī)模緩存系統(tǒng)的負載均衡。**它的工作過程是這樣的,首先根據(jù) ip 或者其他的信息為緩存節(jié)點生成一個 hash,并將這個 hash 投射到 [0, 232 - 1] 的圓環(huán)上。當有查詢或?qū)懭胝埱髸r,則為緩存項的 key 生成一個 hash 值。然后查找第一個大于或等于該 hash 值的緩存節(jié)點,并到這個節(jié)點中查詢或?qū)懭刖彺骓?。如果當前?jié)點掛了,則在下一次查詢或?qū)懭刖彺鏁r,為緩存項查找另一個大于其 hash 值的緩存節(jié)點即可。大致效果如下圖所示,每個緩存節(jié)點在圓環(huán)上占據(jù)一個位置。如果緩存項的 key 的 hash 值小于緩存節(jié)點 hash 值,則到該緩存節(jié)點中存儲或讀取緩存項。比如下面綠色點對應(yīng)的緩存項將會被存儲到 cache-2 節(jié)點中。由于 cache-3 掛了,原本應(yīng)該存到該節(jié)點中的緩存項最終會存儲到cache-4節(jié)點中。

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖4 一致性哈希算法

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

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

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖5 數(shù)據(jù)傾斜

當節(jié)點很少的時候可能會出現(xiàn)這樣的分布情況,A服務(wù)會承擔大部分請求。這種情況就叫做數(shù)據(jù)傾斜。

那如何解決數(shù)據(jù)傾斜的問題呢?

加入虛擬節(jié)點。

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

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖6 引入虛擬節(jié)點

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

一致性哈希算法中,加入虛擬節(jié)點,可以解決數(shù)據(jù)傾斜問題。

四、一致性哈希在DUBBO 中的應(yīng)用

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖7 Dubbo中一致性哈希環(huán)

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

Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊

圖8 數(shù)據(jù)傾斜問題

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

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

public class ConsistentHashLoadBalance extends AbstractLoadBalance {


    private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = 
        new ConcurrentHashMap<String, ConsistentHashSelector<?>>();


    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> 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<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
        // 如果 invokers 是一個新的 List 對象,意味著服務(wù)提供者數(shù)量發(fā)生了變化,可能新增也可能減少了。
        // 此時 selector.identityHashCode != identityHashCode 條件成立
        if (selector == null || selector.identityHashCode != identityHashCode) {
            // 創(chuàng)建新的 ConsistentHashSelector
            selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));
            selector = (ConsistentHashSelector<T>) selectors.get(key);
        }


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

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

private static final class ConsistentHashSelector<T> {


    // 使用 TreeMap 存儲 Invoker 虛擬節(jié)點
    private final TreeMap<Long, Invoker<T>> virtualInvokers;


    private final int replicaNumber;


    private final int identityHashCode;


    private final int[] argumentIndex;


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

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

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

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


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


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

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

到此關(guān)于 ConsistentHashLoadBalance 就分析完了。

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

五、應(yīng)用場景

  • DNS負載均衡最早的負載均衡技術(shù)是通過DNS來實現(xiàn)的,在DNS中為多個地址配置同一個名字,因而查詢這個名字的客戶機將得到其中一個地址,從而使得不同的客戶訪問不同的服務(wù)器,達到負載均衡的目的。DNS負載均衡是一種簡單而有效的方法,但是它不能區(qū)分服務(wù)器的差異,也不能反映服務(wù)器的當前運行狀態(tài)。
  • 代理服務(wù)器負載均衡使用代理服務(wù)器,可以將請求轉(zhuǎn)發(fā)給內(nèi)部的服務(wù)器,使用這種加速模式顯然可以提升靜態(tài)網(wǎng)頁的訪問速度。然而,也可以考慮這樣一種技術(shù),使用代理服務(wù)器將請求均勻轉(zhuǎn)發(fā)給多臺服務(wù)器,從而達到負載均衡的目的。
  • 地址轉(zhuǎn)換網(wǎng)關(guān)負載均衡支持負載均衡的地址轉(zhuǎn)換網(wǎng)關(guān),可以將一個外部IP地址映射為多個內(nèi)部IP地址,對每次TCP連接請求動態(tài)使用其中一個內(nèi)部地址,達到負載均衡的目的。
  • 協(xié)議內(nèi)部支持負載均衡除了這三種負載均衡方式之外,有的協(xié)議內(nèi)部支持與負載均衡相關(guān)的功能,例如HTTP協(xié)議中的重定向能力等,HTTP運行于TCP連接的最高層。
  • NAT負載均衡NAT(Network Address Translation網(wǎng)絡(luò)地址轉(zhuǎn)換)簡單地說就是將一個IP地址轉(zhuǎn)換為另一個IP地址,一般用于未經(jīng)注冊的內(nèi)部地址與合法的、已獲注冊的Internet IP地址間進行轉(zhuǎn)換。適用于解決Internet IP地址緊張、不想讓網(wǎng)絡(luò)外部知道內(nèi)部網(wǎng)絡(luò)結(jié)構(gòu)等的場合下。
  • 反向代理負載均衡普通代理方式是代理內(nèi)部網(wǎng)絡(luò)用戶訪問internet上服務(wù)器的連接請求,客戶端必須指定代理服務(wù)器,并將本來要直接發(fā)送到internet上服務(wù)器的連接請求發(fā)送給代理服務(wù)器處理。反向代理(Reverse Proxy)方式是指以代理服務(wù)器來接受internet上的連接請求,然后將請求轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡(luò)上的服務(wù)器,并將從服務(wù)器上得到的結(jié)果返回給internet上請求連接的客戶端,此時代理服務(wù)器對外就表現(xiàn)為一個服務(wù)器。反向代理負載均衡技術(shù)是把將來自internet上的連接請求以反向代理的方式動態(tài)地轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡(luò)上的多臺服務(wù)器進行處理,從而達到負載均衡的目的。
  • 混合型負載均衡在有些大型網(wǎng)絡(luò),由于多個服務(wù)器群內(nèi)硬件設(shè)備、各自的規(guī)模、提供的服務(wù)等的差異,可以考慮給每個服務(wù)器群采用最合適的負載均衡方式,然后又在這多個服務(wù)器群間再一次負載均衡或群集起來以一個整體向外界提供服務(wù)(即把這多個服務(wù)器群當做一個新的服務(wù)器群),從而達到最佳的性能。將這種方式稱之為混合型負載均衡。此種方式有時也用于單臺均衡設(shè)備的性能不能滿足大量連接請求的情況下。

作者:京東物流 喬杰

來源:京東云開發(fā)者社區(qū)文章來源地址http://www.zghlxwxcb.cn/news/detail-480971.html

到了這里,關(guān)于Dubbo負載均衡策略之 一致性哈希 | 京東云技術(shù)團隊的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包