文章目錄
- 一、前置知識
- 二、Ribbon核心API
- 三、自定義負載均衡政策IRule
- 1、編寫IRule實作類
- 2、編寫Ribbon配置類
- 3、應用到全部服務上(Ribbon全局配置)
- 1)Spring的自動掃描
- 2)@RibbonClients注解
- 3、應用到指定服務上(Ribbon局部配置)
- 1)代碼配置 -- @RibbonClient
- 2)屬性配置 -- application.yml
- 3)兩種方式對比:
- 4)**細粒度配置-最佳實踐:**
- 4、使用浏覽器進行調用服務消費者
- 四、自定義服務執行個體是否存活判定政策IPing
- 1、自定義IPing
- 2、修改Ribbon配置類
- 五、性能優化-饑餓加載
一、前置知識
在前一篇文章【雲原生&微服務一】SpringCloud之Ribbon實作負載均衡詳細案例(內建Eureka、Ribbon)我們讨論了SpringCloud如何內建Eureka和Ribbon,本文就在其基礎上讨論一下如何自定義Ribbon的負載均衡政策、以及Ribbon的核心API。
二、Ribbon核心API
部落客習慣性的在深入研究一門技術的時候去GitHub上看文檔,然而Ribbon在GitHub上的文檔(https://github.com/Netflix/ribbon)真的是沒啥可看的;就給了一個demo和Release notes。
Ribbon有三個核心接口:ILoadBalancer、IRule、IPing,其中:
- ILoadBalancer是負載均衡器;
- IRule 複雜負載均衡的規則,ILoadBalancer根據其選擇一個可用的Server伺服器;
- IPing負責定時ping每個伺服器,判斷其是否存活。
三、自定義負載均衡政策IRule
1、編寫IRule實作類
MyRule重寫IRule的
choose(Object o)
方法,每次都通路
List<Server>
中第一個服務執行個體;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
import java.util.List;
/**
* 自定義負載均衡規則,隻用第一個執行個體;
*
* @author Saint
*/
public class MyRule implements IRule {
private ILoadBalancer loadBalancer;
@Override
public Server choose(Object o) {
final List<Server> allServers = this.loadBalancer.getAllServers();
return allServers.get(0);
}
@Override
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.loadBalancer = iLoadBalancer;
}
@Override
public ILoadBalancer getLoadBalancer() {
return loadBalancer;
}
}
注意:一般很少需要自己定制負載均衡算法的,除非是類似hash分發的那種場景,可以自己寫個自定義的Rule,比如說,每次都根據某個請求參數,分發到某台機器上去。不過在分布式系統中,盡量減少這種需要hash分發的情況。
下面我接着看如何把自定義的MyRule應用到指定的服務上 或 全部服務上。
2、編寫Ribbon配置類
在Ribbon配置類中通過@Bean注解将自定義的IRule實作類MyRule注入到Spring容器中。
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自定義Ribbon配置
*
* @author Saint
*/
@Configuration
public class MyRibbonConfiguration {
@Bean
public IRule getRule() {
return new MyRule();
}
}
3、應用到全部服務上(Ribbon全局配置)
Ribbon全局配置有兩種方式:一種是依賴Spring的自動掃描、一種是依賴
@RibbonClients
注解。
1)Spring的自動掃描
所謂Spring的自動掃描,就是将自定義的Ribbon配置類放在Spring容器可以掃描到的包目錄下即可。
如上圖所示,程式的啟動類RibbonFeignSampleConsumerApplication所在的目錄為com.saint,Ribbon配置類
MyRibbonConfiguration
所在的目錄為com.saint.config;又因沒有指定包掃描的路徑,是以目錄會掃描啟動類所在的包com.saint,是以Spring可以自動掃描到MyRibbonConfiguration、進而掃描到MyRule。
注意:Ribbon的配置類一定不能Spring掃描到。因為Ribbon有自己的子上下文,Spring的父上下文如果和Ribbon的子上下文重疊,會有各種各樣的問題。比如:Spring和SpringMVC父子上下文重疊會導緻事務不生效。
是以不推薦使用這種方式。
2)@RibbonClients注解
在啟動類所在目錄的父目錄(com.saint)中建立config檔案夾(com.config),并将
MyRibbonConfiguration
類移動到其中,代碼目錄結構如下:
這樣操作之後,Ribbon配置類講不會被Spring掃描到。是以需要利用
@RibbonClients
注解做一些配置;
在com.saint.config目錄下新增GreetingServiceRibbonConf類:
package com.saint.config;
import com.config.MyRibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;
/**
* @author Saint
*/
@Configuration
@RibbonClients(defaultConfiguration = MyRibbonConfiguration.class)
public class GreetingServiceRibbonConf {
}
3、應用到指定服務上(Ribbon局部配置)
針對Ribbon局部配置,有兩種方式:代碼配置 和 屬性配置,上面提到的
@RibbonClients
就屬于代碼配置的方式,差別在于Ribbon局部配置使用的是
@RibbonClient
注解;
1)代碼配置 – @RibbonClient
将GreetingServiceRibbonConf類的内容修改如下:
package com.saint.config;
import com.config.MyRibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Configuration;
/**
* 自定義 調用greeting-service服務時 使用的配置
*
* @author Saint
*/
@Configuration
@RibbonClient(name = "GREETING-SERVICE", configuration = MyRibbonConfiguration.class)
public class GreetingServiceRibbonConf {
}
當然我們也可以不使用GreetingServiceRibbonConf作為一個配置類,直接将
@RibbonClient(name = "GREETING-SERVICE", configuration = MyRibbonConfiguration.class)
加在啟動類中也是一樣的。
2)屬性配置 – application.yml
首先将
GreetingServiceRibbonConf
類中的内容全部注釋掉:
然後在application.yml檔案中添加如下内容:
# GREETING-SERVICE為要調用的微服務名
GREETING-SERVICE:
ribbon:
NFLoadBalancerRuleClassName:
3)兩種方式對比:
- 代碼配置:基于代碼、更加靈活;但是線上修改得重新打包、釋出,并且還有小坑(父子上下文問題)
- 屬性配置: 配置更加直覺、優先級更高(相對代碼配置)、線上修改無需重新打包、釋出;但是極端場景下沒有代碼配置方式靈活。
注意:如果代碼配置和屬性配置兩種方式混用,屬性配置優先級更高。
4)細粒度配置-最佳實踐:
- 盡量使用屬性配置,屬性方式實作不了的情況下再考慮代碼配置。
- 同一個微服務内盡量保持單一性,使用同樣的配置方式,避免兩種方式混用,增加定位代碼的複雜性。
4、使用浏覽器進行調用服務消費者
結合博文:【雲原生&微服務一】SpringCloud之Ribbon實作負載均衡詳細案例(內建Eureka、Ribbon),我們已經依次啟動了eureka-server、ribbon-feign-sample-8081、ribbon-feign-sample-8082、ribbon-feign-sample-consumer;三個服務、四個執行個體。
此處我們針對服務消費者
ribbon-feign-sample-consumer
做四次接口調用,分别為:
- http://localhost:9090/say/saint
- http://localhost:9090/say/saint2
- http://localhost:9090/say/saint3
- http://localhost:9090/say/saint4
然後我們去看ribbon-feign-sample-8081、ribbon-feign-sample-8082的控制台輸出:1> ribbon-feign-sample-8081控制台輸出:
2> ribbon-feign-sample-8082控制台輸出:
3> 結果說明:
我們可以發現,四個請求,ribbon-feign-sample-8082執行個體處理了所有的請求,我們自定義的IRule已經生效。
四、自定義服務執行個體是否存活判定政策IPing
和IRule的自定義方式一樣,這裡隻提供自定義的IPing,具體配置方式和IRule一樣。
1、自定義IPing
package com.saint.config;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.Server;
/**
* 自定義IPing,判斷每個服務是否還存活
* @author Saint
*/
public class MyPing implements IPing {
@Override
public boolean isAlive(Server server) {
return true;
}
}
2、修改Ribbon配置類
package com.config;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.saint.config.MyPing;
import com.saint.config.MyRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自定義Ribbon配置
*
* @author Saint
*/
@Configuration
public class MyRibbonConfiguration {
@Bean
public IRule getRule() {
return new MyRule();
}
@Bean
public IPing getPing() {
return new MyPing();
}
}
五、性能優化-饑餓加載
ribbon:
eager-load:
# 開啟饑餓加載
enabled: true
# 開啟饑餓加載的微服務清單,多個以,分隔
clients: user-center,xxx