1. 程式人生 > >幾種負載均衡演算法簡介

幾種負載均衡演算法簡介

首先我們來看一下什麼是負載均衡?

隨著業務的發展,單臺web伺服器已經承載不了系統現在流量的時候,我們就需要部署多臺伺服器,將流量分散在不同的伺服器上,這樣可以提高系統的可用性。

我們可以對web請求進行負載均衡,很大的一部分原因是由於HTTP協議的無狀態性,同樣的請求響應是一樣的,所以哪個伺服器處理都可以(先不考慮session,考慮session的情況,參見我的另一篇部落格)

好了,廢話不多說,我們來看一下有哪些負載均衡演算法:
1.簡單輪詢

顧名思義,這個方法是對後端web伺服器進行簡單的輪詢,將請求按順序發給後端伺服器。
但是我們知道,伺服器的效能可能是不一樣的,每個伺服器此刻處理的請求數量也是不一樣的,這樣簡單地輪詢,可能滿足不了我們系統的要求。因此這個演算法一般作為學習使用。

public class RoundRobin {
  private static Map<String, Integer> serviceWeightMap = new ConcurrentHashMap<>();

  static {
    serviceWeightMap.put("192.168.1.100", 1);
    serviceWeightMap.put("192.168.1.101", 1);
    serviceWeightMap.put("192.168.1.102", 4);
    serviceWeightMap.put("192.168.1.103", 1);
    serviceWeightMap.put("192.168.1.104", 1);
    serviceWeightMap.put("192.168.1.105", 3);
    serviceWeightMap.put("192.168.1.106", 1);
    serviceWeightMap.put("192.168.1.107", 2);
    serviceWeightMap.put("192.168.1.108", 1);
    serviceWeightMap.put("192.168.1.109", 1);
    serviceWeightMap.put("192.168.1.110", 1);
  }

  private static Integer pos = 0;

  public static String testRoundRobin() {
    Set<String> keySet = serviceWeightMap.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;
  }
}

程式碼中為了保證執行緒安全,對伺服器列表做了快照,但是這樣又會引發新的問題,比如我們新增伺服器或者某個伺服器掛掉了,那麼我們的輪詢無法感知。

此外,程式碼中引入了重量級的同步方式synchronized,這樣對與高併發量的業務來說,效能提高不上去。

2.隨機演算法
顧名思義,該演算法就是每來一個請求,從後端伺服器中隨機地選擇一個伺服器處理請求,程式碼如下:

 public static String testRandom() {
    Set<String> keySet = serviceWeightMap.keySet();
    ArrayList<String> keyList = new ArrayList<String>();
    keyList.addAll(keySet);

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

3.源地址雜湊法
該方法是對客戶端的IP地址做一個Hash,然後對伺服器列表的大小取模,得到處理該請求的伺服器

採用源地址雜湊法進行負載均衡,相同的IP客戶端,如果伺服器列表不變,將對映到同一個後臺伺服器進行訪問

  public static String testConsumerHash(String remoteIp) {
    Set<String> keySet = serviceWeightMap.keySet();
    ArrayList<String> keyList = new ArrayList<String>();
    keyList.addAll(keySet);

    int hashCode = remoteIp.hashCode();
    int pos = hashCode % keyList.size();

    return keyList.get(pos);
  }

4.加權輪詢法
該方法是在輪詢的基礎上加一個權重,權重高的伺服器處理的請求就多:

 public static String testWeightRoundRobin() {
    Set<String> keySet = serviceWeightMap.keySet();
    Iterator<String> it = keySet.iterator();
    List<String> serverList = new ArrayList<String>();

    while (it.hasNext()) {
      String server = it.next();
      Integer weight = serviceWeightMap.get(server);
      for (int i=0; i<weight; i++) {
        serverList.add(server);
      }
      Collections.shuffle(serverList);
    }

    String server = null;
    synchronized (pos) {
      if (pos > serverList.size()) {
        pos = 0;
      }
      server = serverList.get(pos);
      pos++;
    }
    return server;
  }

5.加權隨機法:
該演算法是在隨機演算法的基礎上加一個權重:

 public static String testWeightRandom() {
    Set<String> keySet = serviceWeightMap.keySet();
    List<String> serverList = new ArrayList<String>();
    Iterator<String> it = keySet.iterator();

    while (it.hasNext()) {
      String server = it.next();
      Integer weight = serviceWeightMap.get(server);
      for (int i=0; i<weight; i++) {
        serverList.add(server);
      }
    }
    Random random = new Random();
    int randomPos = random.nextInt(serverList.size());
    String server = serverList.get(randomPos);
    return server;
  }

6.最小連線法:
該演算法是檢測那一臺伺服器上的連線數最小,就將請求發給那一臺伺服器:
由於檢測伺服器上連線的程式碼較為複雜,這裡先不列出
思路:先輪詢一遍伺服器列表,得到連線數最小的伺服器,即為處理該請求的伺服器