原文連接:https://www.javazhiyin.com/5130.htmlhtml
微服務開發專欄:https://www.javazhiyin.com/category/springcloudjava
Ribbon是什麼?mysql
Ribbon是基於Netflix Ribbon實現的一套客戶端 負載均衡的工具。nginx
簡單的說,Ribbon是Netflix發佈的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務鏈接在一塊兒。Ribbon客戶端組件提供一系列完善的配置項如鏈接超時,重試等。git
簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)後面全部的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機鏈接等)去鏈接這些機器。咱們也很容易使用Ribbon實現自定義的負載均衡算法。github
LB,即負載均衡(Load Balance),在微服務或分佈式集羣中常常用的一種應用。
負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA。 算法
常見的負載均衡有軟件Nginx,LVS,硬件 F5等。spring
相應的在中間件,例如:dubbo和SpringCloud中均給咱們提供了負載均衡,SpringCloud的負載均衡算法能夠自定義。sql
集中式LB:數據庫
即在服務的消費方和提供方之間使用獨立的LB設施(能夠是硬件,如F5, 也能夠是軟件,如nginx), 由該設施負責把訪問請求經過某種策略轉發至服務的提供方。
進程內LB:
將LB邏輯集成到消費方,消費方從服務註冊中心獲知有哪些地址可用,而後本身再從這些地址中選擇出一個合適的服務器。
Ribbon就屬於進程內LB,它只是一個類庫,集成於消費方進程,消費方經過它來獲取到服務提供方的地址。
https://github.com/Netflix/ribbon/wiki/Getting-Started
1.修改microservicecloud-consumer-dept-80工程
1).修改pom文件,增長如下內容
<!-- Ribbon相關 --> <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.修改application.yml 追加eureka的服務註冊地址
2).修改application.yml資源文件,增長如下內容:
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.對ConfigBean類進行新註解@LoadBalanced 得到Rest時加入Ribbon的配置。
3).修改ConfigBean類,增長如下內容:
@Bean @LoadBalanced //要求客戶端經過Rest去訪問微服務的時候自帶負載均衡 public RestTemplate getRestTemplate(){ return new RestTemplate(); }
4.主啓動類DeptConsumer80_App添加@EnableEurekaClient註解。
4).修改主啓動類DeptConsumer80_App,增長如下內容:
@SpringBootApplication @EnableEurekaClient public class DeptConsumer80_App { public static void main(String[] args) { SpringApplication.run(DeptConsumer80_App.class, args); } }
5.修改DeptController_Consumer客戶端訪問類。
5).修改DeptController_Consumer,增長如下內容:
/private static final String REST_URL_PREFIX = "http://localhost:8001"; //從Eureka上面有一個叫MICROSERVICECLOUD-DEPT微服務名字,按名字訪問的微服務 private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";
6.測試:
1).先啓動3個eureka集羣,也就是microservicecloud-eureka-7001,microservicecloud-eureka-7002和microservicecloud-eureka-7003。
2).再啓動microservicecloud-provider-dept-8001並註冊進eureka。
3).再啓動microservicecloud-consumer-dept-80。
輸入到瀏覽器: http://localhost/consumer/dept/get/1 進行測試
輸入到瀏覽器: http://localhost/consumer/dept/list 進行測試
輸入到瀏覽器: http://localhost/consumer/dept/add?dname=大數據部 進行測試
查看咱們的mysql數據庫看是否添加了大數據部:
添加成功效果圖:
總結:Ribbon和Eureka整合後Consumer能夠直接調用服務而不用再關心地址和端口號。
Ribbon負載均衡架構圖:
Ribbon在工做時分紅兩步
第一步先選擇 EurekaServer ,它優先選擇在同一個區域內負載較少的server.
第二步再根據用戶指定的策略,在從server取到的服務註冊列表中選擇一個地址。
其中Ribbon提供了多種策略:好比輪詢、隨機和根據響應時間加權。
1.在總父工程下建立microservicecloud-provider-dept-8002的maven Module
1).而後再參考microservicecloud-provider-dept-8001的pom文件,將pom文件裏的配置複製一份到microservicecloud-provider-dept-8002 pom文件中
2.在總父工程下再建立microservicecloud-provider-dept-8003的maven Module
1).而後再參考microservicecloud-provider-dept-8001的pom文件,將pom文件裏的配置複製一份到microservicecloud-provider-dept-8003 pom文件中
3.將microservicecloud-provider-dept-8001的java源碼複製兩份分別給microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003,而且分別修改主啓動類名字
修改爲功後的效果圖:
4.而後再將microservicecloud-provider-dept-8001工程下的resources資源文件夾裏面的兩個配置文件都複製到microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003
效果圖以下:
而後咱們修改microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003的application.yml文件裏面的端口,都分別改成8002和8003端口。
5.新建兩個數據庫給microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003這兩個工程鏈接和使用
備註:建立cloudDB02數據庫,執行如下sql語句
DROP DATABASE IF EXISTS cloudDB02; CREATE DATABASE cloudDB02 CHARACTER SET UTF8; USE cloudDB02; CREATE TABLE 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 dept;
再建立一個名爲cloudDB03的數據庫,執行如下sql語句。
DROP DATABASE IF EXISTS cloudDB03; CREATE DATABASE cloudDB03 CHARACTER SET UTF8; USE cloudDB03; CREATE TABLE 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 dept;
而後再改microservicecloud-provider-dept-8002和microservicecloud-provider-dept-8003這兩個工程的application.yml文件下的數據庫源的url指定的數據庫名字
microservicecloud-provider-dept-8002工程連 cloudDB02,
microservicecloud-provider-dept-8003工程連 cloudDB03。
好了,咱們作了那麼多配置和修改,如今要進入測試咱們的負載均衡了!!!!
測試的步驟:
1.啓動3個eureka集羣配置(microservicecloud-eureka-7001系列)
2.啓動3個Dept微服務並各自測試經過(microservicecloud-provider-dept-8001系列)
啓動3個Dept微服務成功後先進行一次測試
1).先在瀏覽器輸入第一個網址:http://localhost:8001/dept/list 進行測試
2).再從瀏覽器中輸入第二個網址:http://localhost:8002/dept/list 進行測試
3).再從瀏覽器中輸入第三個網址:http://localhost:8003/dept/list 進行測試
若是三個網址都成功出現JSON字符串,而且db_source的數據都不一樣,那麼說明成功了!!!
3.啓動microservicecloud-consumer-dept-80
啓動成功之後,在瀏覽器輸入:http://localhost/consumer/dept/list
就能夠體現負載均衡的效果了。
啓動成功後的負載均衡效果圖: 這是第一次訪問的結果,
咱們在http://localhost/consumer/dept/list 這個頁面上刷新一次該頁面就會看到clouddb03會變成clouddb02再刷新一次就會變成clouddb01。依次這樣,這種模式採用的是輪詢算法,這裏就不演示了。
備註:注意觀察看到返回的數據庫名字,各不相同,負載均衡實現
而後再打開http://eureka7001.com:7001/ 你就能夠看到在一個微服務下面掛着三個實例
總結:Ribbon其實就是一個軟負載均衡的客戶端組件,他能夠和其餘所需請求的客戶端結合使用,和eureka結合只是其中的一個實例。
根據特定算法中從服務列表中選取一個要訪問的服務。
SpringCloud結合Ribbon,它默認出廠自帶了7種算法。
第一種是:RoundRobinRule 輪詢
第二種是:RandomRule 隨機
第三種是:AvailabilityFilteringRule 會先過濾掉因爲屢次訪問故障而處於斷路器跳閘狀態的服務,
還有併發的鏈接數量超過閾值的服務,而後對剩餘的服務列表按照輪詢策略進行訪問。
第四種是:WeightedResponseTimeRule 根據平均響應時間計算全部服務的權重,響應時間越快服務權重越大被選中的機率越高。剛啓動時若是統計信息不足,則使用RoundRobinRule策略,等統計信息足夠,
會切換到WeightedResponseTimeRule。
第五種是:RetryRule 先按照RoundRobinRule的策略獲取服務,若是獲取服務失敗則在指定時間內會進行重試,獲取可用的服務。
第六種是:BestAvailableRule 會先過濾掉因爲屢次訪問故障而處於斷路器跳閘狀態的服務,而後選擇一個併發量最小的服務。
第七種是:ZoneAvoidanceRule 默認規則,複合判斷server所在區域的性能和server的可用性選擇服務器。
那麼目前,咱們實現了第一種算法RoundRobinRule 輪詢的方式。
那咱們就來作第二種隨機算法的實現吧。
在microservicecloud-consumer-dept-80工程ConfigBean類下作一個隨機算法的方法
@Bean public IRule myRule(){ //用從新選擇的隨機算法替代默認的輪詢算法。 return new RandomRule(); }
咱們來測試一下:
1.啓動3個eureka集羣配置(microservicecloud-eureka-7001系列)。
2.啓動3個Dept微服務並各自測試經過(microservicecloud-provider-dept-8001系列)。
3.啓動microservicecloud-consumer-dept-80工程進行隨機算法的測試。
請在瀏覽器輸入:http://localhost/consumer/dept/list
隨機的效果我不截圖了,由於用了隨機算法,隨機的效果的體現大家就能體會的到。
那麼其餘的一些算法,我就不一一進行展現了。
若是上面的7種算法,都不夠出來業務邏輯,那麼能夠來自定義算法。
1.修改microservicecloud-consumer-dept-80的主啓動類
1).在主啓動類上添加一個註解@RibbonClient()
/*在啓動該微服務的時候就能去加載咱們的自定義Ribbon配置類,從而使配置生效。
而且官方文檔明確給出了警告:
這個自定義配置類不能放在@ComponentScan所掃描的當前包下以及子包下,
不然咱們自定義的這個配置類就會被全部的Ribbon客戶端所共享,也就是說
咱們達不到特殊化定製的目的了。*/
//那麼解決方案是從新在java包下再建一個包名,並把MySelfRule類放入該包內。
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
請看下面的效果圖:
編寫MySelfRule自定義配置類的:
@Configuration public class MySelfRule{ @Bean public IRule myRule() { return new RandomRule();//Ribbon默認是輪詢,我自定義爲隨機 } }
而後進行測試:
1.啓動3個eureka集羣配置(microservicecloud-eureka-7001系列)。
2.啓動3個Dept微服務並各自測試經過(microservicecloud-provider-dept-8001系列)。
3.啓動microservicecloud-consumer-dept-80工程進行隨機算法的測試。
請在瀏覽器輸入:http://localhost/consumer/dept/get/1
測試的效果我不截圖了,由於用的是自定義隨機算法,隨機的效果的實現大家就能體會的到。
需求:依舊輪詢策略,可是加上新需求,每一個服務器要求被調用5次。也即之前是每臺機器一次,如今是每臺機器5次。
需求代碼實現: 在MySelfRule類中的myRule方法裏面添加如下內容:
@Configuration public class MySelfRule { @Bean public IRule myRule() { //return new RandomRule();//Ribbon默認是輪詢,自定義爲隨機 return new RandomRule(); }
而後在myrule包下建立一個RandomRule_hhf的類,而且添加如下內容:
解析源碼:https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
//需求是5次,可是微服務只有8001,8002,8003三臺機器
public class RandomRule_hhf extends AbstractLoadBalancerRule {
//總共被調用的次數,目前要求每臺被調用5次
private int total = 0;
//當前提供服務的機器號
private int currentIndex = 0;
public Server choose(ILoadBalancer lb, Object key) {
//ILoadBalancer哪種負載均衡算法若是等於null的話就返回null,那麼天然而然,它確定會加載一種算法,因此它不會變成null。
if (lb == null) {
return null;
}
//如今還不知道是哪一個算法來響應server
Server server = null;
//若是說server等於null,那麼就看線程是否中斷了,若是被中斷的話就返回null
while (server == null) {
if (Thread.interrupted()) {
return null;
}
//upList的意思就是如今活着的能夠對外提供的機器,而後.get()方法經過
//int index = rand.nextInt(serverCount); 那麼就是隨機到幾就返回第幾的值
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
//若是serverCount目前有三臺,那麼就不等於0,那麼就是flase。
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
//這個的意思就是說若是serverCount有三臺,那麼index就獲得了從下標0和1和2數組
// int index = rand.nextInt(serverCount);
// server = upList.get(index);
//當第一次total < 5的時候
//當第二次total < 5的時候
//當第五次total < 5的時候(那麼第五次就不小於5),那麼if(total < 5)這段裏面的代碼就不執行了
if(total < 5){
//那麼第一次的server是0號機
//那麼第二次的server也是0號機
server = upList.get(currentIndex);
//第一次的總的計數次數是加一個1
//第二次的總的計數次數是再加一個1
total++;
//當第五次total < 5的時候就走else
}else {
//那麼total等於0
total = 0;
//而currentIndex就加一個1
currentIndex++;
//那麼1大於等於upList.size(),目前假設有三臺機器,那麼1就不大於等於upList.size()
//那麼如今就是給0號機給1號機進行服務了,以此類推。。
//可是若是currentIndex等於下標3的時候而且>= upList.size(),但咱們按照數組下標來算的話只 //有0和1和2的下標,那麼當currentIndex等於下標3的時候這樣就是超過第三臺了,那麼 //currentIndex就從新等於0。以此類推。。
if(currentIndex >= upList.size())
{
currentIndex = 0;
}
}
//若是這個server等於null,那麼線程中斷一會,下一輪繼續
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;
}
//若是活着好好的,那麼就返回server回去
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
//返回對應該響應服務是8001,仍是8002仍是8003
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
那麼咱們的自定義算法定義好了之後,咱們再回到MySelfRule類中添加RandomRule_hhf類進行自定義算法的測試了
在MySelfRule類中添加RandomRule_hhf類:
@Configuration
public class MySelfRule {
@Bean
public IRule myRule()
{
//return new RandomRule();//Ribbon默認是輪詢,自定義爲隨機
//return new RandomRule();
return new RandomRule_hhf(); //自定義每臺機器5次
}
}
進入測試:
1.啓動3個eureka集羣配置(microservicecloud-eureka-7001系列)。
2.啓動3個Dept微服務並各自測試經過(microservicecloud-provider-dept-8001系列)。
3.啓動microservicecloud-consumer-dept-80工程進行自定義負載均衡算法的測試。
請在瀏覽器輸入:http://localhost/consumer/dept/get/1
測試的效果我不截圖了,只要在頁面刷新5次便可看出效果!!