Ribbon是Spring Cloud Netflix全家桶中負責負載均衡的組件,它是一組類庫的集合。經過Ribbon,程序員能在不涉及到具體實現細節的基礎上「透明」地用到負載均衡,而沒必要在項目裏過多地編寫實現負載均衡的代碼。java
好比,在某個包含Eureka和Ribbon的集羣中,某個服務(能夠理解成一個jar包)被部署在多臺服務器上,當多個服務使用者同時調用該服務時,這些併發的請求能被用一種合理的策略轉發到各臺服務器上。程序員
事實上,在使用Spring Cloud的其它各類組件時,咱們都能看到Ribbon的痕跡,好比Eureka能和Ribbon整合,而在後文裏將提到的提供網關功能Zuul組件在轉發請求時,也能夠整合Ribbon從而達到負載均衡的效果。服務器
從代碼層面來看,Ribbon有以下三個比較重要的接口。架構
第一,ILoadBalancer,這也叫負載均衡器,經過它,咱們能在項目里根據特定的規則合理地轉發請求,常見的實現類有BaseLoadBalancer。併發
第二,IRule,這個接口有多個實現類,好比RandomRule和RoundRobinRule,這些實現類具體地定義了諸如「隨機「和」輪詢「等的負載均衡策略,咱們還能重寫該接口裏的方法來自定義負載均衡的策略。負載均衡
在BaseLoadBalancer類裏,咱們能經過IRule的實現類設置負載均衡的策略,這樣該負載均衡器就能據此合理地轉發請求。dom
第三,IPing接口,經過該接口,咱們能獲取到當前哪些服務器是可用的,咱們也能經過重寫該接口裏的方法來自定義判斷服務器是否可用的規則。在BaseLoadBalancer類裏,咱們一樣能經過IPing的實現類設置判斷服務器是否可用的策略。 函數
在Ribbon裏,咱們還能夠經過ILOadBalancer這個接口以基於特定的負載均衡策略來選擇服務器。spa
經過下面的ILoadBalancerDemo.java,咱們來看下這個接口的基本用法。這個類是放在4.2部分建立的RabbionBasicDemo項目裏,代碼以下。 code
1 //省略必要的package和import代碼 2 public class ILoadBalancerDemo { 3 public static void main(String[] args){ 4 //建立ILoadBalancer的對象 5 ILoadBalancer loadBalancer = new BaseLoadBalancer(); 6 //定義一個服務器列表 7 List<Server> myServers = new ArrayList<Server>(); 8 //建立兩個Server對象 9 Server s1 = new Server("ekserver1",8080); 10 Server s2 = new Server("ekserver2",8080); 11 //兩個server對象放入List類型的myServers對象裏 12 myServers.add(s1); 13 myServers.add(s2); 14 //把myServers放入負載均衡器 15 loadBalancer.addServers(myServers); 16 //在for循環裏發起10次調用 17 for(int i=0;i<10;i++){ 18 //用基於默認的負載均衡規則得到Server類型的對象 19 Server s = loadBalancer.chooseServer("default"); 20 //輸出IP地址和端口號 21 System.out.println(s.getHost() + ":" + s.getPort()); 22 } 23 } 24 }
在第5行裏,咱們建立了BaseLoadBalancer類型的loadBalancer對象,而BaseLoadBalancer是負載均衡器ILoadBalancer接口的實現類。
在第6到第13行裏,咱們建立了兩個Server類型的對象,並把它們放入了myServers裏,在第15行裏,咱們把List類型的myServers對象放入了負載均衡器裏。
在第17到22行的for循環裏,咱們經過負載均衡器模擬了10次選擇服務器的動做,具體而言,是在第19行裏,經過loadBalancer的chooseServer方法以默認的負載均衡規則選擇服務器,在第21行裏,咱們是用「打印」這個動做來模擬實際的「使用Server對象處理請求」的動做。
上述代碼的運行結果以下所示,其中咱們能看到,loadBalancer這個負載均衡器把10次請求均攤到了2臺服務器上,從中確實能看到 「負載均衡」的效果。
第二,IRule,這個接口有多個實現類,好比RandomRule和RoundRobinRule,這些實現類具體地定義了諸如「隨機「和」輪詢「等的負載均衡策略,咱們還能重寫該接口裏的方法來自定義負載均衡的策略。
在BaseLoadBalancer類裏,咱們能經過IRule的實現類設置負載均衡的策略,這樣該負載均衡器就能據此合理地轉發請求。
第三,IPing接口,經過該接口,咱們能獲取到當前哪些服務器是可用的,咱們也能經過重寫該接口裏的方法來自定義判斷服務器是否可用的規則。在BaseLoadBalancer類裏,咱們一樣能經過IPing的實現類設置判斷服務器是否可用的策略。
1 ekserver2:8080 2 ekserver1:8080 3 ekserver2:8080 4 ekserver1:8080 5 ekserver2:8080 6 ekserver1:8080 7 ekserver2:8080 8 ekserver1:8080 9 ekserver2:8080 10 ekserver1:8080
在Ribbon裏,咱們能夠經過定義IRule接口的實現類來給負載均衡器設置相應的規則。在下表裏,咱們能看到IRule接口的一些經常使用的實現類。
實現類的名字 |
負載均衡的規則 |
RandomRule |
採用隨機選擇的策略 |
RoundRobinRule |
採用輪詢策略 |
RetryRule |
採用該策略時,會包含重試動做 |
AvailabilityFilterRule |
會過濾些屢次鏈接失敗和請求併發數太高的服務器 |
WeightedResponseTimeRule |
根據平均響應時間爲每一個服務器設置一個權重,根據該權重值優先選擇平均響應時間較小的服務器 |
ZoneAvoidanceRule |
優先把請求分配到和該請求具備相同區域(Zone)的服務器上 |
在下面的IRuleDemo.java的程序裏,咱們來看下IRule的基本用法。
1 //省略必要的package和import代碼 2 public class IRuleDemo { 3 public static void main(String[] args){ 4 //請注意這是用到的是BaseLoadBalancer,而不是ILoadBalancer接口 5 BaseLoadBalancer loadBalancer = new BaseLoadBalancer(); 6 //聲明基於輪詢的負載均衡策略 7 IRule rule = new RoundRobinRule(); 8 //在負載均衡器裏設置策略 9 loadBalancer.setRule(rule); 10 //以下定義3個Server,並把它們放入List類型的集合中 11 List<Server> myServers = new ArrayList<Server>(); 12 Server s1 = new Server("ekserver1",8080); 13 Server s2 = new Server("ekserver2",8080); 14 Server s3 = new Server("ekserver3",8080); 15 myServers.add(s1); 16 myServers.add(s2); 17 myServers.add(s3); 18 //在負載均衡器裏設置服務器的List 19 loadBalancer.addServers(myServers); 20 //輸出負載均衡的結果 21 for(int i=0;i<10;i++){ 22 Server s = loadBalancer.chooseServer(null); 23 System.out.println(s.getHost() + ":" + s.getPort()); 24 } 25 } 26 }
這段代碼和上文裏的ILoadBalancerDemo.java很類似,但有以下的差異點。
1 在第5行裏,咱們是經過BaseLoadBalancer這個類而不是接口來定義負載均衡器,緣由是該類包含setRule方法。
2 在第7行定義了一個基於輪詢規則的rule對象,並在第9行裏把它設置進負載均衡器。
3 在第19行裏,咱們是把包含3個Server的List對象放入負載均衡器,而不是以前的兩個。因爲這裏存粹是爲了演示效果,因此咱們就放入一個根本不存在的「ekserver3」服務器。
運行該程序後,咱們能夠看到有10次輸出,並且確實是按「輪詢」的規則有順序地輸出3個服務器的名字。若是咱們把第7行的代碼改爲以下,那麼就會看到 「隨機」地輸出服務器名。
IRule rule = new RandomRule();
在項目裏,咱們通常會讓ILoadBalancer接口自動地判斷服務器是否可用(這些業務都封裝在Ribbon的底層代碼裏),此外,咱們還能夠用Ribbon組件裏的IPing接口來實現這個功能。
在下面的IRuleDemo.java代碼裏,咱們將演示IPing接口的通常用法。
1 //省略必要的package和import代碼 2 class MyPing implements IPing { 3 public boolean isAlive(Server server) { 4 //若是服務器名是ekserver2,則返回false 5 if (server.getHost().equals("ekserver2")) { 6 return false; 7 } 8 return true; 9 } 10 }
第2行定義的MyPing類實現了IPing接口,並在第3行重寫了其中的isAlive方法。
在這個方法裏,咱們根據服務器名來判斷,具體而言,若是名字是ekserver2,則返回false,表示該服務器不可用,不然返回true,表示當前服務器可用。
11 public class IRuleDemo { 12 public static void main(String[] args) { 13 BaseLoadBalancer loadBalancer = new BaseLoadBalancer(); 14 //定義IPing類型的myPing對象 15 IPing myPing = new MyPing(); 16 //在負載均衡器裏使用myPing對象 17 loadBalancer.setPing(myPing); 18 //一樣是建立三個Server對象並放入負載均衡器 19 List<Server> myServers = new ArrayList<Server>(); 20 Server s1 = new Server("ekserver1", 8080); 21 Server s2 = new Server("ekserver2", 8080); 22 Server s3 = new Server("ekserver3", 8080); 23 myServers.add(s1); 24 myServers.add(s2); 25 myServers.add(s3); 26 loadBalancer.addServers(myServers); 27 //經過for循環屢次請求服務器 28 for (int i = 0; i < 10; i++) { 29 Server s = loadBalancer.chooseServer(null); 30 System.out.println(s.getHost() + ":" + s.getPort()); 31 } 32 } 33 }
在第12行的main函數裏,咱們在第15行建立了IPing類型的myPing對象,並在第17行把這個對象放入了負載均衡器。經過第18到第26行的代碼,咱們建立了三個服務器,並把它們也放入負載均衡器。
在第28行的for循環裏,咱們依然是請求並輸出服務器名。因爲這裏的負載均衡器loadBalancer中包含了一個IPing類型的對象,因此在根據策略獲得服務器後,會根據myPing裏的isActive方法來判斷該服務器是否可用。
因爲在這個方法裏,咱們定義了ekServer2這臺服務器不可用,因此負載均衡器loadBalancer對象始終不會把請求發送到該服務器上,也就是說,在輸出結果中,咱們不會看到「ekserver2:8080」的輸出。
從中咱們能看到IPing接口的通常用法,咱們能夠經過重寫其中的isAlive方法來定義「判斷服務器是否可用「的邏輯,在實際項目裏,判斷的依據無非是」服務器響應是否時間過長「或」發往該服務器的請求數是否過多「,而這些判斷方法都封裝在IRule接口以及它的實現類裏,因此在通常的場景中咱們用到IPing接口。
在本週的後面時間裏,我將繼續給出用Eureka+Ribbon高可用負載均衡架構的搭建方法。
本文內容摘自本人寫的專業書籍,轉載時請同時引入該版權申明,請勿用於商業用途。