Spring Cloud(四)Ribbon負載均衡
阿新 • • 發佈:2018-12-16
1 概述
1.1 Ribbon簡介
Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡工具。Ribbon會自動幫助你基於某種規則(簡單輪詢、隨機連線等),也可以實現自定義的負載均衡演算法。
1.2 負載均衡
- 英文名稱:Load Balance,微服務或分散式叢集中常用的一種應用。
- 簡單來說負載均衡就是將使用者的請求ping平攤的分配到多個任務上,從而是系統達到HA(高可用)。
- 兩種負載均衡:
- 集中式LB:偏硬體,服務的消費方和提供方之間使用獨立的LB設施(可以是硬體F5,也可以是軟體Nginx),由該設施負責把訪問請求以某種策略轉發至服務的提供方。
- 程序內LB:偏軟體, 將LB邏輯整合到消費方,消費方從服務註冊中心指導哪些地址可用,再自己選擇一個合適的伺服器。Ribbon就是一個程序內LB,它只是個類庫,集成於消費方程序,消費方通過它獲取服務提供方的地址。
1.3 架構圖
2 Ribbon配置
Ribbon是客戶端負載均衡工具!!!所以應該配置在客戶端
2.1 新建consumer ribbon工程
- 加入依賴,因為Riboon需要依賴Eureka執行,所以要同時加入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>
- 對訪問模版的實現類加入@LoadBalanced註解。
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
- 在application.yml檔案中配置向註冊中心註冊,如果是作為消費者模組不提供服務,不應該註冊自己。
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
register-with-eureka: false #作為消費者不提供服務,不應該註冊自己
- 主啟動類中加入@EnableEurekaClient註解。
@SpringBootApplication
@EnableEurekaClient
public class SpringCloudConsumerRibbonApplication80 {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsumerRibbonApplication80.class, args);
}
}
- 以上步驟1~4完成後即可在controller中直接通過服務名訪問系統中的微服務,服務名作為URI。
private static final String REST_URL_PREFIX= "http://spring-cloud-provider/";
2.2 構建provider叢集
- 構建叢集,新開兩個provider模組,將原provider的程式碼部分和pom.xml中依賴照搬到新的provider中。
- 將原provider中application.yml檔案照搬到新provider,並修改埠號,若新的provider使用自己的資料庫,則修改資料庫資訊(其他配置也一樣,如修改別名instance-id)。
- 叢集中服務名稱必須一致!!!不要修改。
spring:
application:
name: spring-cloud-provider #同一叢集下必須使用同一服務名!!!!!
- 依次啟動3個uereka、三個provider、consumer ribbon服務,測試:
可以看到,三個peovider交替提供服務,證明了Ribbon預設使用輪訓負載均衡演算法。
3 Ribbon核心元件IRule
IRule:根據特定演算法從服務列表中選取一個需要訪問的服務
3.1 七大方法
IRule是一個介面,七大方法是其自帶的落地實現類。
- RoundRobinRule:輪詢(預設方法)
- RandomRule:隨機
- AvailabilityFilteringRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連線數量超過閾值的服務,然後對剩餘的服務進行輪詢
- WeightedResponseTimeRule:根據平均響應時間計算服務的權重。統計資訊不足時會按照輪詢,統計資訊足夠會按照響應的時間選擇服務
- RetryRule:正常時按照輪詢選擇服務,若過程中有服務出現故障,在輪詢一定次數後依然故障,則會跳過故障的服務繼續輪詢。
- BestAvailableRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
- ZoneAvoidanceRule:預設規則,符合判斷server所在的區域的效能和server的可用性選擇服務
3.2 切換規則方法
只需在配置類中配置一個返回具體方法的bean即可
@Bean
public IRule myRule(){
return new RandomRule();
}
3.3 自定義Ribbon負載均衡演算法
3.3.1 自定義Ribbon演算法
自定義的Ribbon演算法類不能放在主啟動類所在的包及子報下(確切來說是不能放在@ComponentScan註解的包及子包下),否則會被全域性應用到Ribbon服務中。應該把自定義演算法類放在另外新建的包下,且這個類應該是為配置類。也可以將該類排除掉(其實與普通切換負載均衡規則類似,只不過是位置不同而已,普通的可以放在主啟動類所在的包,自定義的要放在外面的包下)。
- 定義排除註解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcludeAnno {
}
- 自定義規則類。
/**
* @author: 陳玉林
* @modifiedBy:
* @date: Create In 10:52 2018/10/19
* @description: 每個服務被輪訓五次
*/
public class MyRibbonRule 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);
// private int total = 0; // 總共被呼叫的次數,目前要求每臺被呼叫5次
// private int currentIndex = 0; // 當前提供服務的機器號
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
}
}
- 將自定義規則加入spring容器。
/**
* @author: 陳玉林
* @modifiedBy:
* @date: Create In 10:43 2018/10/19
* @description: 該類必須放置在掃描不到的包下, 或者新增排除, 雖然他必須有註解
*/
@Configuration
@ExcludeAnno
public class MyRibbonConfig {
@Bean
public IRule ribbonRule() {
return new MyRibbonRule();
}
}
- 主啟動類新增@RibbonClient(name = “微服務名”,configuration = XXX.class)註解指定需要用到負載均衡的微服務名及自定義演算法的class物件。然後將該配置類從啟動掃描類排除掉。
// 使用此規則不可放在 可掃描的路徑下, 如果非要放置, 需要加自定義註解
@RibbonClient(name = "spring-cloud-provider", configuration = MyRibbonConfig.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {ExcludeAnno.class})})
public class SpringCloudConsumerRibbonApplication80 {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConsumerRibbonApplication80.class, args);
}
}
- 去掉切換的規則。
- 依次啟動3個uereka、三個provider、consumer ribbon服務,測試,成功。