Nginx學習筆記2-負載均衡算法

首先要了解爲什麼使用負載均衡

Nginx 實現了動靜分離 反向代理

這裏再強調一下正向代理和反向代理。下面通俗的講一下---

正向代理:就是用戶找了個代理,例如vpn之類的,把用戶自己藏起來了。

反向代理:服務器找了個代理藏起來了,外界看不到其真實的地址。

 

當然 如果使用硬件實現負載均衡的話是 非常快的,且可靠性高,併發量大。當然缺點是太貴成本高。

常用的實現負載均衡的軟件Nginx和apache http service。但是apache http service 效率和Nginx比起來效率一般。

所以着重使用Nginx。

Nginx的下載

https://nginx.org/

進入後選擇一個版本,一般來說選擇穩定的版本(stable version),點擊windows版本的則開始下載,解壓開始使用。

當然 使用前需要進行一定的配置 conf裏的nginx.conf, 一般來說做以下修改。

這樣啓動服務輸入url就可以訪問了。

負載均衡算法

1.輪詢:這是比較常用的算法。     先模擬集羣中ip的集合---

package distributed;

import java.util.HashMap;

public class IPMap {

   public static HashMap<String, Integer> serverWeightMap = new HashMap<String, Integer>();

    static {
        //第一個參數是IP地址,第二個是權重.
        serverWeightMap.put("192.168.1.100", 1);
        serverWeightMap.put("192.168.1.101", 2);
        serverWeightMap.put("192.168.1.102", 3);
        serverWeightMap.put("192.168.1.103", 4);
        serverWeightMap.put("192.168.1.104", 3);
        serverWeightMap.put("192.168.1.105", 2);
        serverWeightMap.put("192.168.1.106", 1);
        serverWeightMap.put("192.168.1.107", 2);
        serverWeightMap.put("192.168.1.108", 1);
        serverWeightMap.put("192.168.1.109", 4);
    }
}

而輪詢其實就是按順序均等分配請求,代碼如下。

public class RoundRobin {

    private static Integer pos=0;

    public static String getServerIP(){

        //重新在線程本地copy一份IPMap, 避免服務器上線下線導致的併發問題
        Map<String, Integer>  serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();

        List<String> iplist = new ArrayList<String>();
        iplist.addAll(ips);

        String server = null;

        synchronized (pos){
            if(pos > iplist.size())
                pos=0;

            server = iplist.get(pos);

            pos++;
        }
        return server;
    }
}

由於serverWeightMap中的地址列表是動態的,隨時可能有機器上線、下線或者宕機,因此爲了避免可能出現的併發問題,方法內部要新建局部變量serverMap,現將serverMap中的內容複製到線程本地,以避免被多個線程修改。這樣可能會引入新的問題,複製以後serverWeightMap的修改無法反映給serverMap,也就是說這一輪選擇服務器的過程中,新增服務器或者下線服務器,負載均衡算法將無法獲知。新增無所謂,如果有服務器下線或者宕機,那麼可能會訪問到不存在的地址。因此,服務調用端需要有相應的容錯處理,比如重新發起一次server選擇並調用。

輪詢法的優點在於:試圖做到請求轉移的絕對均衡。

輪詢法的缺點在於:爲了做到請求轉移的絕對均衡,必須付出相當大的代價,因爲爲了保證pos變量修改的互斥性,需要引入重量級的悲觀鎖synchronized,這將會導致該段輪詢代碼的併發吞吐量發生明顯的下降。

2.隨機輪詢

public class RandomIP {

    public static String getServerIP() {

        //重新在線程本地copy一份IPMap, 避免服務器上線下線導致的併發問題
        Map<String, Integer> serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();

        List<String> iplist = new ArrayList<String>();
        iplist.addAll(ips);

        String server = null;

        //獲取IP的策略
        Random random = new Random();
        int pos = random.nextInt(iplist.size());
        return iplist.get(pos);
    }

}

隨着調用量的增大,這種算法也就接近於上面所說的輪詢了。

3.源地址哈希法

public class HashIP {

    public static String getServerIP() {

        //重新在線程本地copy一份IPMap, 避免服務器上線下線導致的併發問題
        Map<String, Integer> serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();

        List<String> iplist = new ArrayList<String>();
        iplist.addAll(ips);

        //獲取IP的策略
        //獲取遠端請求的IP地址
        String remoteIP = "127.0.0.11";
        int hashCode = remoteIP.hashCode();
        hashCode = Math.abs(hashCode);//確保hash值是正數. 如果hash值是負數
        int ipSize = iplist.size();
        int pos = hashCode % ipSize;

        return iplist.get(pos);
    }

}

源地址哈希法的優點在於:保證了相同客戶端IP地址將會被哈希到同一臺後端服務器,直到後端服務器列表變更。根據此特性可以在服務消費者與服務提供者之間建立有狀態的session會話。

源地址哈希算法的缺點在於:除非集羣中服務器的非常穩定,基本不會上下線,否則一旦有服務器上線、下線,那麼通過源地址哈希算法路由到的服務器是服務器上線、下線前路由到的服務器的概率非常低,如果是session則取不到session,如果是緩存則可能引發”雪崩”。(緩存雪崩指:某些原因導致緩存全部失效,意味着所有的數據請求都會到達數據庫,導致數據庫請求暴增,導致數據庫掛掉。)

4.加權輪詢

這個算法是受到服務器配置以及負載情況影響的。就是能者多勞的意思。但是實際情況中服務器一般配置都會保持一樣,所以這種算法用的也並不多。

public class WeightRoundRobin {

    private static Integer pos=0;

    public static String getServerIP(){

        //重新在線程本地copy一份IPMap, 避免服務器上線下線導致的併發問題
        Map<String, Integer>  serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();
        Iterator<String> iterator = ips.iterator();

        List<String> iplist = new ArrayList<String>();
        while (iterator.hasNext()){
            String server = iterator.next();
            int weight = serverMap.get(server);
            //按照權重來添加比例.
            for(int i=0; i<weight; i++){
                iplist.add(server);
            }
        }

        String server=null;
        synchronized (pos){
            if(pos > iplist.size())
                pos=0;

            server = iplist.get(pos);

            pos++;
        }
        return server;
    }
}

5.加權隨機

這個也很好理解,就是隨機到服務器的概率有所增大。

public class WeightRandom {

    public static String getServerIP() {

        //重新在線程本地copy一份IPMap, 避免服務器上線下線導致的併發問題
        Map<String, Integer>  serverMap = new HashMap<String, Integer>();
        serverMap.putAll(IPMap.serverWeightMap);

        //取的IP地址的Set
        Set<String> ips = serverMap.keySet();
        Iterator<String> iterator = ips.iterator();

        List<String> iplist = new ArrayList<String>();
        while (iterator.hasNext()){
            String server = iterator.next();
            int weight = serverMap.get(server);
            //按照權重來添加比例.
            for(int i=0; i<weight; i++){
                iplist.add(server);
            }
        }

        //獲取IP的策略
        Random random = new Random();
        int pos = random.nextInt(iplist.size());
        return iplist.get(pos);
    }
}

6.最小連接數法

前面幾種方法費盡心思來實現服務消費者請求次數分配的均衡,當然這麼做是沒錯的,可以爲後端的多臺服務器平均分配工作量,最大程度地提高服務器的利用率,但是實際情況是否真的如此?實際情況中,請求次數的均衡真的能代表負載的均衡嗎?這是一個值得思考的問題。

上面的問題,再換一個角度來說就是:以後端服務器的視角來觀察系統的負載,而非請求發起方來觀察。最小連接數法便屬於此類。

最小連接數算法比較靈活和智能,由於後端服務器的配置不盡相同,對於請求的處理有快有慢,它正是根據後端服務器當前的連接情況,動態地選取其中當前積壓連接數最少的一臺服務器來處理當前請求,儘可能地提高後端服務器的利用效率,將負載合理地分流到每一臺機器。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章