Spring Cloud Ribbon 自定義負(fù)載均衡策略

原由

公司項(xiàng)目使用Spring Cloud微服務(wù)架構(gòu),隨著服務(wù)的增加,開發(fā)調(diào)試變得有些麻煩。有些同事的電腦配置不高,無法在本地啟動(dòng)這么多的服務(wù)。公司有自己的dev環(huán)境,對(duì)于開發(fā)當(dāng)前修改的服務(wù)可以直接注冊(cè)到dev環(huán)境,使用其他未修改的服務(wù),如Eureka,config等。但是,如果這個(gè)時(shí)候有前端正在dev調(diào)試,則會(huì)出現(xiàn)網(wǎng)關(guān)轉(zhuǎn)發(fā)到本地開發(fā)中的服務(wù),出現(xiàn)異常。

出現(xiàn)上述情況的原因是因?yàn)镽ibbon默認(rèn)負(fù)載均衡策略是輪詢,當(dāng)一個(gè)服務(wù)出現(xiàn)多個(gè)實(shí)例的時(shí)候,網(wǎng)關(guān)轉(zhuǎn)發(fā)或者服務(wù)消費(fèi)時(shí)就會(huì)采用Ribbon的負(fù)載均衡策略,出現(xiàn)指向開發(fā)本地實(shí)例的情況。

知道原因之后解決方法就呼之欲出了:自定義負(fù)載均衡策略,使dev環(huán)境中的微服務(wù)消費(fèi)或轉(zhuǎn)發(fā)都指定到固定dev環(huán)境中服務(wù),不讓其指定到開發(fā)本地起的服務(wù)。

實(shí)現(xiàn)策略

com.netflix.loadbalancer.IRule是Ribbon的負(fù)載均衡策略接口:

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

Ribbon自帶有幾種策略實(shí)現(xiàn):

  • RandomRule:隨機(jī)選取負(fù)載均衡策略,隨機(jī)Random對(duì)象,在所有服務(wù)實(shí)例中隨機(jī)找一個(gè)服務(wù)的索引號(hào),然后從上線的服務(wù)中獲取對(duì)應(yīng)的服務(wù)。
  • RoundRobinRule:線性輪詢負(fù)載均衡策略。
  • WeightedResponseTimeRule:響應(yīng)時(shí)間作為選取權(quán)重的負(fù)載均衡策略,根據(jù)平均響應(yīng)時(shí)間計(jì)算所有服務(wù)的權(quán)重,響應(yīng)時(shí)間越短的服務(wù)權(quán)重越大,被選中的概率越高。剛啟動(dòng)時(shí),如果統(tǒng)計(jì)信息不足,則使用線性輪詢策略,等信息足夠時(shí),再切換到WeightedResponseTimeRule。
  • RetryRule:使用線性輪詢策略獲取服務(wù),如果獲取失敗則在指定時(shí)間內(nèi)重試,重新獲取可用服務(wù)。
  • ClientConfigEnabledRoundRobinRule:默認(rèn)通過線性輪詢策略選取服務(wù)。通過繼承該類,并且對(duì)choose方法進(jìn)行重寫,可以實(shí)現(xiàn)更多的策略,繼承后保底使用RoundRobinRule策略。
  • BestAvailableRule:繼承自ClientConfigEnabledRoundRobinRule。從所有沒有斷開的服務(wù)中,選取到目前為止請(qǐng)求數(shù)量最小的服務(wù)。
  • PredicateBasedRule:抽象類,提供一個(gè)choose方法的模板,通過調(diào)用AbstractServerPredicate實(shí)現(xiàn)類的過濾方法來過濾出目標(biāo)的服務(wù),再通過輪詢方法選出一個(gè)服務(wù)。
  • AvailabilityFilteringRule:按可用性進(jìn)行過濾服務(wù)的負(fù)載均衡策略,會(huì)先過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務(wù),還有并發(fā)的連接數(shù)超過閾值的服務(wù),然后對(duì)剩余的服務(wù)列表進(jìn)行線性輪詢。
  • ZoneAvoidanceRule:本身沒有重寫choose方法,用的還是抽象父類PredicateBasedRule的choose。

其中沒有我們需要的策略,那我們就自己實(shí)現(xiàn)一個(gè)IRule。我們參照默認(rèn)的RoundRobinRule,繼承AbstractLoadBalancerRule(實(shí)現(xiàn)了IRule的loadBanlancer相關(guān)方法):


import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author hubert
 * @version 1.0
 * @date 2019/9/5 上午10:22
 */
@Slf4j
public class MyRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    }

    @Override
    public Server choose(Object o) {
        log.info("key:" + o);
        List<Server> allServers = getLoadBalancer().getAllServers();
        log.info(allServers.toString());
        return allServers.get(0);
    }
}

我們需要實(shí)現(xiàn)choose方法來完成我們自己的策略,getLoadBalancer()可以獲取當(dāng)前服務(wù)的所有實(shí)例Server的信息,我們需要從中挑選一個(gè)作為choose方法的返回。這里就簡(jiǎn)單地返回列表第一個(gè)Server。

配置

自定義策略實(shí)現(xiàn)之后需要配置,我們要在服務(wù)調(diào)用方(使用@FeignClient注解的類的方法)進(jìn)行配置。

簡(jiǎn)單配置

如果所有調(diào)用服務(wù)的策略是相同的,我們最簡(jiǎn)單的配置就是在MyRule類上添加@Component注解,讓Spring發(fā)現(xiàn)并注入該類。Ribbon會(huì)優(yōu)先使用我們實(shí)現(xiàn)的策略。

如果針對(duì)不同的服務(wù)需要不同的策略,則可以參考官方實(shí)例的配置。

@RibbonClient配置

這種方式需要在啟動(dòng)類中添加注解:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@RibbonClient(value = "service-hi", configuration = RuleConfig.class)
public class ServiceFeignApplication {

RuleConfig如下:

import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author hubert
 * @version 1.0
 * @date 2019/9/5 上午10:40
 */
@Configuration
public class RuleConfig {
    @Bean
    public IRule ribbonRule() {
        return new MyRule();
    }
}

簡(jiǎn)單來說就是注入我們自己實(shí)現(xiàn)的IRule,然后配置給RioonClient。

@RibbonClientvalue/name屬性設(shè)置的是被調(diào)用的服務(wù)名(不是當(dāng)前正在配置的服務(wù)名),也即使聲明@FeignClient是指定的value/name。如果當(dāng)前服務(wù)調(diào)用多個(gè)其他服務(wù),可以用@RibbonClient給每個(gè)被調(diào)用服務(wù)設(shè)置不同的策略。

需要注意的是RuleConfig類需要放在啟動(dòng)類的上層(或者不同包名),避免Spring默認(rèn)掃描到。否則會(huì)出現(xiàn)“簡(jiǎn)單配置”效果,即所有服務(wù)都使用這個(gè)策略,無法實(shí)現(xiàn)不同服務(wù)不同策略的效果。

配置文件配置

@RibbonClient類似,可以為每個(gè)微服務(wù)單獨(dú)配置策略。我們?cè)趛ml配置文件中添加<serverName>.ribbon.NFLoadBalancerRuleClassName

service-hi:
  ribbon:
    NFLoadBalancerRuleClassName: com.hubert.feign.MyRule

<serverName>也就是聲明@FeignClient是指定的value/name。

實(shí)驗(yàn)

配置完成之后就是啟動(dòng)實(shí)驗(yàn)了,我們依次啟動(dòng)2個(gè)被調(diào)用服務(wù),以及一個(gè)調(diào)用服務(wù):

2019-09-05 14:31:45.408  INFO 1143 --- [ix-service-hi-2] com.hubert.feign.MyRule                  : key:null
2019-09-05 14:31:45.409  INFO 1143 --- [ix-service-hi-2] com.hubert.feign.MyRule                  : [192.168.31.244:8882, 192.168.31.244:8881]

發(fā)現(xiàn)日志打印,說明使用了我們自定義的策略,并且效果也是始終調(diào)用第一個(gè)服務(wù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容