負載均衡演算法示例
宣告:本文根據魯班學院周瑜老師課程筆記整理而來
負載均衡的介紹?
負載均衡,英文名稱Load Balance,指由多臺伺服器以對稱的方式組成一個伺服器集合,每臺伺服器都具有等價的地位,都可以單獨對外提供服務而無須其他伺服器的輔助。 通過某種負載分擔技術,將外部發送來的請求均勻分佈到對稱結構中的某一臺伺服器上,而接收到請求的伺服器獨立地迴應客戶的請求。 負載均衡能夠平均分配客戶請求到伺服器陣列,藉此提供快速獲取重要資料,解決大量併發訪問伺服器問題,這種叢集技術可以用最少的投資獲得接近於大型主機的效能。 負載均衡方式? 負載均衡分為軟體負載均衡和硬體負載均衡。 軟體負載均衡: 常見的負載均衡軟體有Nginx,LVS,HAProxy。 (總結)Nginx/LVS/HAProxy負載均衡軟體的優缺點:http://www.ha97.com/5646.html 三大主流軟體負載均衡器對比(LVS,Nginx,HAProxy):http://www.21yunwei.com/archives/5824 硬體負載均衡: 常見的負載均衡硬體有Array,F5. 負載均衡演算法? 常見的負載均衡演算法有:隨機演算法,加權輪詢演算法,一致性hash演算法,最小活躍數演算法。演算法的前提條件:
定義一個伺服器列表,每個負載均衡的演算法會從中挑選一個伺服器作為演算法的結果:
/** * 定義一個伺服器列表,每個負載均衡演算法會從中挑選一個伺服器作為演算法結果 * @author Administrator * */ public class ServerIps { public static final List<String> LIST = Arrays.asList( "192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4","192.168.0.5", "192.168.0.6", "192.168.0.7", "192.168.0.8", "192.168.0.9", "192.168.0.10"); }
隨機演算法--RandomLoadBalance
首先來個最簡單的隨機演算法的實現。
/** * 隨機演算法 * @author Administrator * */ public class JavaRandom { public static String getServer(){//生成一個隨機數作為list的下標值 Random random = new Random(); int randomPos = random.nextInt(ServerIps.LIST.size()); return ServerIps.LIST.get(randomPos); } public static void main(String[] args) { //連續呼叫10次 for(int i=0;i<10;i++){ System.out.println(getServer()); } } }
執行結果:
當呼叫次數比較少時,Random產生的隨機數可能會比較集中,此時多數請求會落到同一臺伺服器上,只有在經過多次請求後,才能使用請求進行“均勻”分配,呼叫量少這一點對我們來說不算什麼,因為負載均衡機制正是為了應對請求量多的情況。所以隨機演算法也是用的比較多的一種演算法。
但是,上面的隨機演算法適用於每臺機器的效能差不多的時候,實際上,上產中可能某些機器的效能更高一點,它可以處理更多的請求,所以我們可以對每臺機器設定一個權重。
我們在ServerIps類中增加伺服器權重對應關係的Map,設定權重之和為50。
//增加權重 public static final Map<String,Integer> WEIGHT_LIST=new HashMap<>(); static{ WEIGHT_LIST.put("192.168.0.1", 1); WEIGHT_LIST.put("192.168.0.2", 8); WEIGHT_LIST.put("192.168.0.3", 3); WEIGHT_LIST.put("192.168.0.4", 6); WEIGHT_LIST.put("192.168.0.5", 5); WEIGHT_LIST.put("192.168.0.6", 5); WEIGHT_LIST.put("192.168.0.7", 4); WEIGHT_LIST.put("192.168.0.8", 7); WEIGHT_LIST.put("192.168.0.9", 2); WEIGHT_LIST.put("192.168.0.10", 9); }
那麼現在的隨機演算法應該要改成權重隨機演算法,當呼叫量比較多的時候,伺服器使用的分佈應該近似對應權重的分佈。
權重隨機演算法:
簡單的思路是,把每個伺服器按它對應的伺服器進行復制。
/** * 增加權重的隨機演算法 * @author Administrator * */ public class JavaWeightRandom { public static String getServer(){ //1.首先根據權重進行list陣列的賦值 List<String> ips = new ArrayList<>(); for(String ip : ServerIps.WEIGHT_LIST.keySet()){ Integer weight = ServerIps.WEIGHT_LIST.get(ip); //按權重進行賦值 for(int i = 0;i<weight;i++){ ips.add(ip); } } //2.在list陣列中使用隨機演算法進行取值 //生成一個隨機數作為list的下標值 Random random = new Random(); int randomPos = random.nextInt(ips.size()); return ips.get(randomPos); } public static void main(String[] args) { //連續呼叫10次 for(int i = 0;i<10;i++){ System.out.println(getServer()); } } }
執行結果:
這種實現方法在遇到權重之和特別大的時候,就會比較消耗記憶體,因為需要對IP地址進行復制,權重之和越大,那麼上下文的IPS就需要越多的記憶體,下面介紹另外一種實現思路。
假設我們有一組伺服器servers=[A,B,C],它們對應的權重為weights=[5,3,2],權重總和為10,現在把這些權重值平鋪在一維座標值上,[0,5)區間屬於伺服器A,[5,8)區間屬於伺服器B,[8,10)區間屬於伺服器C,接下來通過隨機數生成器生成一個範圍在[0,10)之間的隨機數,然後計算這個隨機數會落到哪個區間上,比如:數字3會落在伺服器A對應的區間上,此時返回伺服器A即可,權重越大的機器在座標軸上對應的區間範圍就越大,因此隨機數生成器生成的數字就會有更大的概率落到此區間上。只要隨機數生成器產生的隨機數分不行很好,在經過多次選擇之後,每個伺服器被選中的次數比例接近其權重比例。比如,經過一萬次選擇後,伺服器A被選中的次數大約為5000次,伺服器B被選中的次數大約為3000次,伺服器C被選中的次數大約為2000次。
假設現在的隨機數offset=7;
1.offset<5 is false,所以不在[0,5)區間,將offset = offset-5(offset=2)
2.offset<3 is true,所以處在[5,8)區間,所以應該選擇伺服器B
/** * 將權重模擬在一維座標軸上 * @author Administrator * */ public class JavaWeightRandom2 { public static String getServer(){ //定義總權重屬性 int totalWeight = 0; boolean sameWeight = true;//如果所有權重都相等,那麼就隨機一個IP //得到所有的權重,生成一維座標軸 Object[] weights = ServerIps.WEIGHT_LIST.values().toArray(); for(int i =0;i<weights.length;i++){ Integer weight = (Integer) weights[i]; totalWeight += weight; //判斷是否所有權重都相等 if(sameWeight && i>0 && !weight.equals(weights[i-1])){ sameWeight = false; } } //生成一個隨機數作為list的下標值 Random random = new Random(); int randomPos = random.nextInt(totalWeight); if(!sameWeight){//不是所有權重都相等時 for(String ip : ServerIps.WEIGHT_LIST.keySet()){ Integer value = ServerIps.WEIGHT_LIST.get(ip); if(randomPos < value){ return ip; } randomPos = randomPos -value; } } //如果所有權重都相等直接隨機返回一個 return (String) ServerIps.WEIGHT_LIST.keySet().toArray()[new Random().nextInt(ServerIps.WEIGHT_LIST.size())]; } public static void main(String[] args) { //連續呼叫10次 for(int i=0;i<10;i++){ System.out.println(getServer()); } } }
測試結果:
這就是另外一種權重隨機演算法
輪詢演算法--RoundRobinLoadBalance
簡單的輪詢演算法很簡單
/** * 簡單的輪詢演算法 * @author Administrator * */ public class JavaRoundRobin { //當前迴圈的位置 private static Integer pos = 0; public static String getServer(){ String ip = null; //pos同步 synchronized (pos) { if(pos >= ServerIps.LIST.size()){ pos = 0; } ip = ServerIps.LIST.get(pos); pos++; } return ip; } public static void main(String[] args) { for(int i=0;i<10;i++){ System.out.println(getServer()); } } }
測試結果:
這種演算法很簡單也很公平,每臺伺服器輪流來進行服務,但是,有的機器效能好,所以應當能者多勞,和隨機演算法一樣,加上權重這個維度之後,其中一種實現方法就是複製發,這種複製演算法和隨機演算法一樣,缺點都是比較消耗記憶體,那麼有沒有用其他實現方法?我們下面來介紹一種演算法。