【五】Ribbon負載均衡
1、概述
1.1、是什麽
Spring Cloud Ribbon 是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。
簡單的說, Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load BalanCer(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。
1.2、能幹什麽
LB(負載均衡)
LB,即負載均衡( Load Balanoe ),在微服務或分布式集群中經常用的一種應用。
負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA。
常見的負載均衡有軟件nginx , LVS ,硬件 F5 等。
相應的在中間件,例如:dubbo。和 SpringCloud 中均給我們提供了負載均衡,SpringCloud的負載均衡算法可以自定義。
兩種LB
1、集中式LB(偏硬件)
集中式 LB
即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬件,如F5,也可以是軟件,如nginx ) ,由該設施負責把訪問請求通過某種策略轉發至服務的提供方;
2、進程內LB(偏軟件)
進程內 LB
將LB邏輯集成到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選擇出一個合適的服務器。
Ribbon就屬於進程內LB,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服各提供方的地址。
1.3、官方資料
https://github.com/Netflix/ribbon/wiki
2、Ribbon配置初步
1、修改microservicecloud-consumer-dept-80工程POM,添加:
<!-- Ribbon相關 --> <!--Ribbon 需要 eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
2、修改YML,加入Ribbon配置
eureka: client: register-with-eureka: false #自己不能註冊 service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
3、在cn.hfbin.springcloud.cfgbeans.ConfigBean.java中的getRestTemplate()方法上上加入註解@LoadBalanced
@Configuration public class ConfigBean { @Bean @LoadBalanced //Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端 負載均衡的工具。 public RestTemplate getRestTemplate() { return new RestTemplate(); } }
4、主啟動類DeptConsumer80_App添加 @EnableEurekaClient
5、修改客戶端的程序類DeptController_Consumer.java
//把下面這行 private static final String REST_URL_PREFIX = "http://localhost:8001"; //改成下面這行 private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";
6、測試
先啟動單個eureka三個集群再啟動8001,最後啟動80
7、總結
Ribbon整合後80可以直接調用微服務而不再關心地址和端口
Ribbon 在工作時分成兩步第一步先選擇 EurekaServer,它優先選擇在同一個區域內負載較少的 server。
第二步再根據用戶指定的策略,在從server取到的服務註冊列表中選擇一個地址。
其中Ribbon 提供了多種策略:比如輪詢、隨機和根據響應時間加權。
2、新建兩個工程microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003,參考8001
2.1、將8001 POM 內容拷貝到8002 、 8003
2.2、將8001下的java代碼拷貝到8002 、 8003,並修改主啟動類的名稱
2.3、將8001 YML復制到8002 、 8003資源文件下、修改部分如圖:
3.3、新建兩個數據庫springclouddb02 、 springclouddb03
#記得修改數據庫名 CREATE SCHEMA `springclouddb01` DEFAULT CHARACTER SET utf8 ; create table springclouddb01.dept ( deptno bigint not null primary key auto_increment, dname varchar(60), db_source varchar(60) ); insert into dept(dname , db_source) values(‘開發部‘ , database()); insert into dept(dname , db_source) values(‘人事部‘ , database()); insert into dept(dname , db_source) values(‘財務部‘ , database()); insert into dept(dname , db_source) values(‘市場部‘ , database()); insert into dept(dname , db_source) values(‘運維部‘ , database()); select * from springclouddb01.dept;
3.4、測試:
啟動7001—> 7002 —> 7003 —> 8001 —> 8002 ----> 8003 —> 80
訪問連接:http://localhost/consumer/dept/list 註意看返回的json數據,發現每次刷新得到的數據是不一樣的,輪詢的返回數據,這時通過Ribbon完成了負載均衡。
3.5、總結:
Ribbon默認自帶了七種算法,默認是輪詢。
我們也可以自定義自己的算法。
4.1、修改訪問服務的算法方式
* 切換 訪問的算法 很簡單只需要換成我們要返回算法的實例即可
* 默認有七個算法,可以也自定義自己的算法
package cn.hfbin.myrule; /** * 該配置類不可以放在與註解 @ComponentScan 的同包或者子包下,否則不起作用 (自定義算法也是一樣) */ @Configuration public class MySelfRule { /* * 切換 訪問的算法 很簡單只需要換成我們要返回算法的實例即可 * 默認有七個算法,可以自定義自己的算法 * */ @Bean public IRule myRule() { //如果 突然間一個服務掛了 訪問帶掛的服務器會報錯,出現錯誤頁面 //return new RoundRobinRule(); //return new RandomRule(); //達到的目的,用我們重新選擇的隨機算法替代默認的輪詢。 //如果 突然間一個服務掛了 訪問帶掛的服務器會報錯,出現錯誤頁面,但是過一下子他不會再訪問掛的機器,不會顯示出錯誤的頁面。 return new RetryRule(); } }
4.2、自定義IRule算法
1、在主程序添加@RibbonClient(name=“MICROSERVICECLOUD-DEPT”,configuration=MySelfRule.class)
(註意:自定義算法不可以放在與註解 @ComponentScan 的同包或者子包下,否則不起作用 )
2、MySelfRule.java
@Configuration public class MySelfRule { @Bean public IRule myRule() { //return new RandomRule();// Ribbon默認是輪詢,我自定義為隨機 //return new RoundRobinRule();// Ribbon默認是輪詢,我自定義為隨機 //return new RetryRule(); return new RandomRule();// 我自定義為每臺機器5次 } }
3、自定義算法必須繼承抽象類 AbstractLoadBalancerRule
package cn.hfbin.myrule; /* * 參考隨機數的源碼來修改 * * https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java * */ public class RandomRule extends AbstractLoadBalancerRule { // total = 0 // 當total==5以後,我們指針才能往下走, // index = 0 // 當前對外提供服務的服務器地址, // total需要重新置為零,但是已經達到過一個5次,我們的index = 1 // 分析:我們5次,但是微服務只有8001 8002 8003 三臺,OK? // private int total = 0; // 總共被調用的次數,目前要求每臺被調用5次 private int currentIndex = 0; // 當前提供服務的機器號 public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } //活著的可以對外提供服務的機器 List<Server> upList = lb.getReachableServers(); //所有的服務 List<Server> allList = lb.getAllServers(); //服務的總數 int serverCount = allList.size(); if (serverCount == 0) { /* * No servers. End regardless of pass, because subsequent passes only get more * restrictive. */ return null; } // int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3); // server = upList.get(index); if (total < 5) { //獲取服務的第幾個 server = upList.get(currentIndex); total++; } else { total = 0; currentIndex++; if (currentIndex >= upList.size()) { currentIndex = 0; } } if (server == null) { /* * The only time this should happen is if the server list were somehow trimmed. * This is a transient condition. Retry after yielding. */ Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn‘t actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server; } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // TODO Auto-generated method stub } }
4、測試
測試與上面啟動順序一樣
【五】Ribbon負載均衡