溫馨提示×

溫馨提示×

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

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

如何理解多種負(fù)載均衡算法及其Java代碼實(shí)現(xiàn)

發(fā)布時間:2021-11-24 14:07:21 來源:億速云 閱讀:104 作者:柒染 欄目:編程語言

本篇文章為大家展示了如何理解多種負(fù)載均衡算法及其Java代碼實(shí)現(xiàn),內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

首先給大家介紹下什么是負(fù)載均衡

負(fù)載均衡 建立在現(xiàn)有網(wǎng)絡(luò)結(jié)構(gòu)之上,它提供了一種廉價有效透明的方法擴(kuò)展 網(wǎng)絡(luò)設(shè)備和 服務(wù)器的帶寬、增加 吞吐量、加強(qiáng)網(wǎng)絡(luò)數(shù)據(jù)處理能力、提高網(wǎng)絡(luò)的靈活性和可用性。

負(fù)載均衡,英文名稱為Load Balance,其意思就是分?jǐn)偟蕉鄠€操作單元上進(jìn)行執(zhí)行,例如Web 服務(wù)器、 FTP服務(wù)器、 企業(yè)關(guān)鍵應(yīng)用服務(wù)器和其它關(guān)鍵任務(wù)服務(wù)器等,從而共同完成工作任務(wù)。

如何理解多種負(fù)載均衡算法及其Java代碼實(shí)現(xiàn)

本文講述的是”將外部發(fā)送來的請求均勻分配到對稱結(jié)構(gòu)中的某一臺服務(wù)器上”的各種算法,并以Java代碼演示每種算法的具體實(shí)現(xiàn),OK,下面進(jìn)入正題,在進(jìn)入正題前,先寫一個類來模擬Ip列表:

import java.util.HashMap;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */public class IpMap   {// 待路由的Ip列表,Key代表Ip,Value代表該Ip的權(quán)重public static HashMap<String, Integer> serverWeightMap =new HashMap<String, Integer>();static{
        serverWeightMap.put("192.168.1.100", 1);
        serverWeightMap.put("192.168.1.101", 1);// 權(quán)重為4serverWeightMap.put("192.168.1.102", 4);
        serverWeightMap.put("192.168.1.103", 1);
        serverWeightMap.put("192.168.1.104", 1);// 權(quán)重為3serverWeightMap.put("192.168.1.105", 3);
        serverWeightMap.put("192.168.1.106", 1);// 權(quán)重為2serverWeightMap.put("192.168.1.107", 2);
        serverWeightMap.put("192.168.1.108", 1);
        serverWeightMap.put("192.168.1.109", 1);
        serverWeightMap.put("192.168.1.110", 1);
    }
}

輪詢(Round Robin)法

輪詢調(diào)度算法的原理是每一次把來自用戶的請求輪流分配給內(nèi)部中的服務(wù)器,從1開始,直到N(內(nèi)部服務(wù)器個數(shù)),然后重新開始循環(huán)。算法的優(yōu)點(diǎn)是其簡潔性,它無需記錄當(dāng)前所有連接的狀態(tài),所以它是一種無狀態(tài)調(diào)度。

其代碼實(shí)現(xiàn)大致如下:

import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import java.util.Set;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */class RoundRobin   {
    private static Integer pos = 0;

    public static String getServer()
    {// 重建一個Map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問題Map<String, Integer> serverMap =new HashMap<String, Integer>();
        serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();
        ArrayList<String> keyList = new ArrayList<String>();
        keyList.addAll(keySet);String server = null;
        synchronized (pos)
        {if (pos > keySet.size())
                pos = 0;
            server = keyList.get(pos);
            pos ++;
        }return server;
    }
}

由于serverWeightMap中的地址列表是動態(tài)的,隨時可能有機(jī)器上線、下線或者宕機(jī),因此為了避免可能出現(xiàn)的并發(fā)問題,方法內(nèi)部要新建局部變量serverMap,現(xiàn)將serverMap中的內(nèi)容復(fù)制到線程本地,以避免被多個線程修改。這樣可能會引入新的問題,復(fù)制以后serverWeightMap的修改無法反映給serverMap,也就是說這一輪選擇服務(wù)器的過程中,新增服務(wù)器或者下線服務(wù)器,負(fù)載均衡算法將無法獲知。新增無所謂,如果有服務(wù)器下線或者宕機(jī),那么可能會訪問到不存在的地址。因此,服務(wù)調(diào)用端需要有相應(yīng)的容錯處理,比如重新發(fā)起一次server選擇并調(diào)用。

對于當(dāng)前輪詢的位置變量pos,為了保證服務(wù)器選擇的順序性,需要在操作時對其加鎖,使得同一時刻只能有一個線程可以修改pos的值,否則當(dāng)pos變量被并發(fā)修改,則無法保證服務(wù)器選擇的順序性,甚至有可能導(dǎo)致keyList數(shù)組越界。

輪詢法的優(yōu)點(diǎn)在于:試圖做到請求轉(zhuǎn)移的絕對均衡。

輪詢法的缺點(diǎn)在于:為了做到請求轉(zhuǎn)移的絕對均衡,必須付出相當(dāng)大的代價,因?yàn)闉榱吮WCpos變量修改的互斥性,需要引入重量級的悲觀鎖synchronized,這將會導(dǎo)致該段輪詢代碼的并發(fā)吞吐量發(fā)生明顯的下降。

隨機(jī)(Random)法

通過系統(tǒng)的隨機(jī)算法,根據(jù)后端服務(wù)器的列表大小值來隨機(jī)選取其中的一臺服務(wù)器進(jìn)行訪問。由概率統(tǒng)計(jì)理論可以得知,隨著客戶端調(diào)用服務(wù)端的次數(shù)增多,

其實(shí)際效果越來越接近于平均分配調(diào)用量到后端的每一臺服務(wù)器,也就是輪詢的結(jié)果。

隨機(jī)法的代碼實(shí)現(xiàn)大致如下:

import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import java.util.Set;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */

 class Random   {
    public static String getServer()
    {// 重建一個Map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問題   Map<String, Integer> serverMap =new HashMap<String, Integer>();
        serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址List   Set<String> keySet = serverMap.keySet();
        ArrayList<String> keyList = new ArrayList<String>();
        keyList.addAll(keySet);

        java.util.Random random = new java.util.Random();
        int randomPos = random.nextInt(keyList.size());return keyList.get(randomPos);
    }
}

整體代碼思路和輪詢法一致,先重建serverMap,再獲取到server列表。在選取server的時候,通過Random的nextInt方法取0~keyList.size()區(qū)間的一個隨機(jī)值,從而從服務(wù)器列表中隨機(jī)獲取到一臺服務(wù)器地址進(jìn)行返回?;诟怕式y(tǒng)計(jì)的理論,吞吐量越大,隨機(jī)算法的效果越接近于輪詢算法的效果。

源地址哈希(Hash)法

源地址哈希的思想是根據(jù)獲取客戶端的IP地址,通過哈希函數(shù)計(jì)算得到的一個數(shù)值,用該數(shù)值對服務(wù)器列表的大小進(jìn)行取模運(yùn)算,得到的結(jié)果便是客服端要訪問服務(wù)器的序號。采用源地址哈希法進(jìn)行負(fù)載均衡,同一IP地址的客戶端,當(dāng)后端服務(wù)器列表不變時,它每次都會映射到同一臺后端服務(wù)器進(jìn)行訪問。

源地址哈希算法的代碼實(shí)現(xiàn)大致如下:

import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import java.util.Set;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */

 class Hash      {
    public static String getServer()
    {// 重建一個Map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問題Map<String, Integer> serverMap =new HashMap<String, Integer>();
        serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();
        ArrayList<String> keyList = new ArrayList<String>();
        keyList.addAll(keySet);// 在Web應(yīng)用中可通過HttpServlet的getRemoteIp方法獲取String remoteIp = "127.0.0.1";
        int hashCode = remoteIp.hashCode();
        int serverListSize = keyList.size();
        int serverPos = hashCode % serverListSize;return keyList.get(serverPos);
    }
}

前兩部分和輪詢法、隨機(jī)法一樣就不說了,差別在于路由選擇部分。通過客戶端的ip也就是remoteIp,取得它的Hash值,對服務(wù)器列表的大小取模,結(jié)果便是選用的服務(wù)器在服務(wù)器列表中的索引值。

源地址哈希法的優(yōu)點(diǎn)在于:保證了相同客戶端IP地址將會被哈希到同一臺后端服務(wù)器,直到后端服務(wù)器列表變更。根據(jù)此特性可以在服務(wù)消費(fèi)者與服務(wù)提供者之間建立有狀態(tài)的session會話。

源地址哈希算法的缺點(diǎn)在于:除非集群中服務(wù)器的非常穩(wěn)定,基本不會上下線,否則一旦有服務(wù)器上線、下線,那么通過源地址哈希算法路由到的服務(wù)器是服務(wù)器上線、下線前路由到的服務(wù)器的概率非常低,如果是session則取不到session,如果是緩存則可能引發(fā)”雪崩”。如果這么解釋不適合明白,可以看我之前的一篇文章MemCache超詳細(xì)解讀,一致性Hash算法部分。

加權(quán)輪詢(Weight Round Robin)法

不同的后端服務(wù)器可能機(jī)器的配置和當(dāng)前系統(tǒng)的負(fù)載并不相同,因此它們的抗壓能力也不相同。給配置高、負(fù)載低的機(jī)器配置更高的權(quán)重,讓其處理更多的請;而配置低、負(fù)載高的機(jī)器,給其分配較低的權(quán)重,降低其系統(tǒng)負(fù)載,加權(quán)輪詢能很好地處理這一問題,并將請求順序且按照權(quán)重分配到后端。加權(quán)輪詢法的代碼實(shí)現(xiàn)大致如下:

import java.util.*;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */class WeightRoundRobin   {
    private static Integer pos;

    public static String getServer()
    {// 重建一個Map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問題Map<String, Integer> serverMap =new HashMap<String, Integer>();
        serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();
        Iterator<String> iterator = keySet.iterator();

        List<String> serverList = new ArrayList<String>();while (iterator.hasNext())
        {String server = iterator.next();
            int weight = serverMap.get(server);for (int i = 0; i < weight; i++)
                serverList.add(server);
        }String server = null;
        synchronized (pos)
        {if (pos > keySet.size())
                pos = 0;
            server = serverList.get(pos);
            pos ++;
        }return server;
    }
}

與輪詢法類似,只是在獲取服務(wù)器地址之前增加了一段權(quán)重計(jì)算的代碼,根據(jù)權(quán)重的大小,將地址重復(fù)地增加到服務(wù)器地址列表中,權(quán)重越大,該服務(wù)器每輪所獲得的請求數(shù)量越多。

加權(quán)隨機(jī)(Weight Random)法

與加權(quán)輪詢法一樣,加權(quán)隨機(jī)法也根據(jù)后端機(jī)器的配置,系統(tǒng)的負(fù)載分配不同的權(quán)重。不同的是,它是按照權(quán)重隨機(jī)請求后端服務(wù)器,而非順序。

import java.util.*;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */

 class WeightRandom   {
    public static String getServer()
    {// 重建一個Map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問題Map<String, Integer> serverMap =new HashMap<String, Integer>();
        serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();
        Iterator<String> iterator = keySet.iterator();

        List<String> serverList = new ArrayList<String>();while (iterator.hasNext())
        {String server = iterator.next();
            int weight = serverMap.get(server);for (int i = 0; i < weight; i++)
                serverList.add(server);
        }

        java.util.Random random = new java.util.Random();
        int randomPos = random.nextInt(serverList.size());return serverList.get(randomPos);
    }
}

這段代碼相當(dāng)于是隨機(jī)法和加權(quán)輪詢法的結(jié)合,比較好理解,就不解釋了。

最小連接數(shù)(Least Connections)法

最小連接數(shù)算法比較靈活和智能,由于后端服務(wù)器的配置不盡相同,對于請求的處理有快有慢,它是根據(jù)后端服務(wù)器當(dāng)前的連接情況,動態(tài)地選取其中當(dāng)前

積壓連接數(shù)最少的一臺服務(wù)器來處理當(dāng)前的請求,盡可能地提高后端服務(wù)的利用效率,將負(fù)責(zé)合理地分流到每一臺服務(wù)器。

前面幾種方法費(fèi)盡心思來實(shí)現(xiàn)服務(wù)消費(fèi)者請求次數(shù)分配的均衡,當(dāng)然這么做是沒錯的,可以為后端的多臺服務(wù)器平均分配工作量,***程度地提高服務(wù)器的利用率,但是實(shí)際情況是否真的如此?實(shí)際情況中,請求次數(shù)的均衡真的能代表負(fù)載的均衡嗎?這是一個值得思考的問題。

上面的問題,再換一個角度來說就是:以后端服務(wù)器的視角來觀察系統(tǒng)的負(fù)載,而非請求發(fā)起方來觀察。最小連接數(shù)法便屬于此類。

最小連接數(shù)算法比較靈活和智能,由于后端服務(wù)器的配置不盡相同,對于請求的處理有快有慢,它正是根據(jù)后端服務(wù)器當(dāng)前的連接情況,動態(tài)地選取其中當(dāng)前積壓連接數(shù)最少的一臺服務(wù)器來處理當(dāng)前請求,盡可能地提高后端服務(wù)器的利用效率,將負(fù)載合理地分流到每一臺機(jī)器。由于最小連接數(shù)設(shè)計(jì)服務(wù)器連接數(shù)的匯總和感知,設(shè)計(jì)與實(shí)現(xiàn)較為繁瑣,此處就不說它的實(shí)現(xiàn)了。

上述內(nèi)容就是如何理解多種負(fù)載均衡算法及其Java代碼實(shí)現(xiàn),你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

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

AI