原由
公司項(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耐量。
@RibbonClient
的value/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ù)席舍。