SpringCloud Ribbon負載均衡的實現及自定義負載均衡

之前的文章中我們簡單介紹了一下微服務系統架構中引入Ribbon負載均衡的集成
接下來我們會系統的介紹一下Ribbon的實現及自定義策略負載均衡

1)項目的基礎結構:
在這裏插入圖片描述
在一個微服務中我們存在一個共有api
一個統一的客戶端(服務消費者)80
服務註冊中心的集羣 eureka
服務的提供者(多個

Ribbon應用的思維導圖:
在這裏插入圖片描述
Ribbon是一個客戶端實現負載均衡的方案,集成在我們的編碼階段,區別於nginx的不同是不需要搭建服務器來實現
其核心主要爲:

  • 獲取註冊中心的服務
  • 通過用戶定義的負載均衡的策略選擇服務的提供者(默認採用輪詢策略

Ribbon的核心策略類爲IRule:其中包含的策略有
在這裏插入圖片描述
後面我們會具體介紹這幾種策略

2)實現Ribbon負載均衡
在這裏插入圖片描述
根據官方描述:
可以使用@RibbonClient實現Ribbon的負載均衡,但是實現類必須是@Configuration且不能與主啓動類存在與同一級下,所以我們的客戶端項目結構爲
在這裏插入圖片描述
3)修改負載均衡的策略

@Configuration
public class MyRule {

    @Bean
    public IRule myIRule(){
        return new RandomRule(); //隨機
    }

}

4)配置主啓動類使用我們的負載均衡配置

@SpringBootApplication
@EnableEurekaClient
//在啓動時加載我們自定義的負載均衡組件,自定義組件必須是@Configuration,他會替換已有的組件,從而使用我們自定義的負載均衡組件
@RibbonClient(name = "peo_user",configuration = MyRule.class)
public class ConApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConApplication.class,args);
    }

}

至此我們就完成了Ribbon的負載均衡的實現,當然,在實際開發中,IRule提供的負載均衡的策略不一定滿足我們服務器的配置需求,這時候就需要我們實現自定義的負載均衡的算法策略,下面我們就介紹一下Ribbon提供的負載均衡的策略及如何實在自定義的負載均衡策略

上面我們介紹到Ribbon的核心組件是IRule,是所有負載均衡算法的父接口,其下有
在這裏插入圖片描述

  • RoundRobinRule :輪詢策略,即依次使用存活的服務
  • RandomRule : 隨機策略,每次請求會產生一個隨機的服務提供訪問
  • AvailabilityFilteringRule :過濾輪詢策略,他會先過濾掉跳閘、故障、超過併發閾值的服務,對剩餘服務使用輪詢策略
  • WeightedResponseTimeRule : 權重策略,根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大被選中的概率越高。剛啓動時,如果統計信息不足,則使用輪詢策略,等信息足夠,切換到 WeightedResponseTimeRule
  • RetryRule : 重試,先按照輪詢策略獲取服務,如果獲取失敗則在指定時間內重試,獲取可用服務
  • BestAvailableRule :選過濾掉多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
  • ZoneAvoidanceRule : 符合判斷server所在區域的性能和server的可用性選擇服務

以上的幾種策略都繼承AbstractLoadBalancerRule

下面簡單介紹一下隨機策略的實現源碼,其餘實現大家可自行閱讀源碼

public class RandomRule extends AbstractLoadBalancerRule {
    public RandomRule() {
    }

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            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) {
                    return null;
                }

                int index = this.chooseRandomInt(serverCount);//產生一個範圍隨機數
                server = (Server)upList.get(index); //獲得存活的服務
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

通過註解我們可以看出,其實就是實現了一個簡單的算法,通過一個範圍隨機數來指定此次訪問的服務爲哪一個。

重點來了-------
重點來了-------
重點來了-------

既然我們已經瞭解了Ribbon負載均衡的算法實現及原理,那麼我們就可以實現我們自己的負載均衡策略

  • 集成AbstractLoadBalancerRule
  • 添加實現方法
  • 編寫自己的算法策略
  • 依據上面的配置修改爲我們自己的策略

描述:每個服務提供五次訪問,滿五次以後切換到下一個服務,當所有服務都使用完畢後從第一個服務繼續開始,編碼如下

public class MyBalancerRule extends AbstractLoadBalancerRule {

    private int total = 0; //服務被調用次數
    private int currentIndex = 0; //當前提供服務的服務下標

    public MyBalancerRule(){

    }

    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    /**
     * 自定義策略,訪問五次更換服務
     */
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            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) { //如果不存在服務,返回
                    return null;
                }

                if(total < 5){ //服務未被調用五次,可以繼續使用當前服務
                    server = upList.get(currentIndex); //獲取當前服務
                    total++; //增加服務調用次數
                }else{ //服務已被調用五次,需要更換服務
                    total = 0; //歸零服務被調用次數
                    currentIndex++; //下移當前被調用服務下標
                    if(currentIndex > upList.size())currentIndex = 0; //如果當前下標大於現存的服務個數,則從頭開始
                    server = upList.get(currentIndex); //獲取當前服務
                }

                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }
                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }
}
@Configuration
public class MyRule {

    @Bean
    public IRule myIRule(){
        return new MyBalancerRule(); //自定義負載均衡策略
    }

}

我們也可以使用配置文件來配置:

eureka.client.service-url.defaultZone=http://localhost:7001/eureka/,http://localhost:7002/eureka/
server.port=80
spring.application.name=pro_user
springboot-eureka-clent.ribbon.NFLoadBalancerRuleClassName=com.balance.config.MyBalancerRule

補充知識
在非eureka環境下使用Ribbon

#取消Ribbon使用Eureka
ribbon.eureka.enabled=false

#配置Ribbon能訪問 的微服務節點,多個節點用逗號隔開
microservice-provider-user.ribbon.listOfServers=localhost:7001,localhost:7002
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章