自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringCloud OpenFeign整合Ribbon實(shí)現(xiàn)負(fù)載均衡及源碼分析

開發(fā) 架構(gòu)
Ribbon 通過實(shí)現(xiàn)IRule接口,內(nèi)置了多種負(fù)載均衡的策略,默認(rèn)采用的是 RoundRobinRule,即輪詢策略。項(xiàng)目中,服務(wù)節(jié)點(diǎn)的部署可能存在基礎(chǔ)配置、網(wǎng)絡(luò)環(huán)境等的差異,抑或是功能上線需要灰度測試,因此,輪詢策略往往不能滿足實(shí)際需求。

負(fù)載均衡器在分布式網(wǎng)絡(luò)中扮演著非常重要的角色。通過負(fù)載均衡,可以實(shí)現(xiàn)更好的性能和可靠性,同時(shí)提高系統(tǒng)的可擴(kuò)展性和彈性。目前,SpringCloud體系中,主要使用的有兩種:Netflix的Ribbon以及官方推出的LoadBalancer。本文OpenFeign與Ribbon協(xié)同工作的核心流程及源碼分析。

使用篇

更換內(nèi)置策略

策略UML圖

Ribbon 通過實(shí)現(xiàn)IRule接口,內(nèi)置了多種負(fù)載均衡的策略,默認(rèn)采用的是 RoundRobinRule,即輪詢策略。項(xiàng)目中,服務(wù)節(jié)點(diǎn)的部署可能存在基礎(chǔ)配置、網(wǎng)絡(luò)環(huán)境等的差異,抑或是功能上線需要灰度測試,因此,輪詢策略往往不能滿足實(shí)際需求。

ribbon內(nèi)置策略

過修改消費(fèi)者工程的配置文件,或修改消費(fèi)者的啟動類或 JavaConfig 類可以實(shí)現(xiàn)更換負(fù)載均衡策略的目的。

方式一,修改配置文件

修改配置文件,在其中添加如下內(nèi)容,指定要使用的負(fù)載均衡策略 <clientName>. <clientConfigNameSpace>.NFLoadBalancerRuleClassName?!咀ⅲ篶lientName是指服務(wù)提供者的服務(wù)名稱】

該方式的好處是,可以為不同的微服務(wù)指定相應(yīng)的負(fù)載均衡策略。

xxx-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

方式二,修改JavaConfig類

在 JavaConfig 類中添加負(fù)載 Bean 方法。全局所有feign對應(yīng)服務(wù)都可以生效。

@Configuration
public class FeignConfiguration {
    /**
     * 配置隨機(jī)的負(fù)載均衡策略
     * 特點(diǎn):對所有的服務(wù)都生效
     */
    @Bean
    public IRule loadBalancedRule() {
        return new RandomRule();
    }
}

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

如果上述的策略不滿足自身要求,我們還可以自定義負(fù)載均衡策略。需要操作 2 個(gè)步驟:

第一步,集成 AbstractLoadBalancerRule類,AbstractLoadBalancerRule實(shí)現(xiàn)了IRule 中的 setLoadBalancer 和 getLoadBalancer,通過繼承 AbstractLoadBalancerRule ,我們就不需要在自己實(shí)現(xiàn)這兩個(gè)方法,而是把關(guān)注點(diǎn)放在choose方法上,即只關(guān)注如何進(jìn)行服務(wù)的負(fù)載上。

/**
 * 自定義負(fù)載均衡算法
 */
public class CustomRule extends AbstractLoadBalancerRule {

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // 基本上不需要實(shí)現(xiàn)
    }

    @Override
    public Server choose(Object key) {
        // 實(shí)現(xiàn)自己的負(fù)載均衡算法
    }
}

第二步,修改JavaConfig類,使用自定義的負(fù)載均衡策略:

@Configuration
public class FeignConfiguration {
    @Bean
    public IRule loadBalancedRule() {
        return new CustomRule();
    }
}

或者修改配置文件(application.properties 或 application.yml):

xxx:
  ribbon:
    NFLoadBalancerRuleClassName: com.xiaopeng.ribbon.CustomRule

Ribbon 核心組件

Ribbon 主要有五大功能組件:ServerList、Rule、Ping、ServerListFilter、ServerListUpdater。

Ribbon 核心組件

(1)負(fù)載均衡器 LoadBalancer

用于管理負(fù)載均衡的組件。初始化的時(shí)候通過加載 YMAL 配置文件創(chuàng)建出來的。

(2)服務(wù)列表 ServerList

ServerList 主要用來獲取所有服務(wù)的地址信息,并存到本地。根據(jù)獲取服務(wù)信息的方式不同,又分為靜態(tài)存儲和動態(tài)存儲。

  • 靜態(tài)存儲:從配置文件中獲取服務(wù)節(jié)點(diǎn)列表并存儲到本地。
  • 動態(tài)存儲:從注冊中心獲取服務(wù)節(jié)點(diǎn)列表并存儲到本地

(3)服務(wù)列表過濾 ServerListFilter

將獲取到的服務(wù)列表按照過濾規(guī)則過濾。

  • 通過 Eureka 的分區(qū)規(guī)則對服務(wù)實(shí)例進(jìn)行過濾。
  • 比較服務(wù)實(shí)例的通信失敗數(shù)和并發(fā)連接數(shù)來剔除不夠健康的實(shí)例。
  • 根據(jù)所屬區(qū)域過濾出同區(qū)域的服務(wù)實(shí)例。

(4)服務(wù)列表更新 ServerListUpdater

服務(wù)列表更新就是 Ribbon 會從注冊中心獲取最新的注冊表信息。是由這個(gè)接口 ServerListUpdater 定義的更新操作。而它有兩個(gè)實(shí)現(xiàn)類,也就是有兩種更新方式:

  • 通過定時(shí)任務(wù)進(jìn)行更新。由這個(gè)實(shí)現(xiàn)類 PollingServerListUpdater 做到的。
  • 利用 Eureka 的事件監(jiān)聽器來更新。由這個(gè)實(shí)現(xiàn)類 EurekaNotificationServerListUpdater 做到的。

(5)心跳檢測 Ping

IPing 接口類用來檢測哪些服務(wù)可用。如果不可用了,就剔除這些服務(wù)。實(shí)現(xiàn)類主要有這幾個(gè):PingUrl、PingConstant、NoOpPing、DummyPing、NIWSDiscoveryPing。

心跳檢測策略對象 IPingStrategy,默認(rèn)實(shí)現(xiàn)是輪詢檢測。

(6)負(fù)載均衡策略 Rule

源碼分析

我們知道,如果要使用OpenFeign,需要在項(xiàng)目的啟動類增加@EnableFeignClinets注解,接口定義增加@FeignClient注解,這樣一來,@EnableFeignClinets會掃描出每個(gè)加了@FeignClient注解的接口,然后生成對應(yīng)的BeanDefinition,最后重新生成一個(gè)bean class為FeignClientFactoryBean的BeanDefinition,注冊到spring容器。接下來就會根據(jù)BeanDefinition來生成feign客戶端的代理對象。

Feign代理對象生成過程

生成代理對象后,所有的方法調(diào)用的時(shí)候最終都會走InvocationHandler接口的實(shí)現(xiàn),默認(rèn)就是ReflectiveFeign.FeignInvocationHandler。而我們今天要重點(diǎn)分析的就是,F(xiàn)eignInvocationHandler是如何實(shí)現(xiàn)遠(yuǎn)程及負(fù)載均衡的。

Feign動態(tài)代理調(diào)用實(shí)現(xiàn)rpc流程分析

FeignInvocationHandler對于invoke方法的實(shí)現(xiàn)。

前幾個(gè)if判斷很簡單,就是判斷是不是調(diào)用的方法是不是equals,hashCode,toString,因?yàn)檫@些方法的調(diào)是不需要走rpc調(diào)用的。

接下就是從dispatch獲取要調(diào)用的方法對應(yīng)的MethodHandler,然后調(diào)用MethodHandler的invoke方法。那MethodHandler是什么時(shí)候生成的呢?MethodHandler是在構(gòu)建動態(tài)代理的時(shí)候生成的。那MethodHandler作用是什么呢?你可以理解為最終rpc的調(diào)用都是基于這個(gè)MethodHandler來實(shí)現(xiàn)的,每個(gè)方法都有對應(yīng)MethodHandler來實(shí)現(xiàn)rpc調(diào)用,接下來我們就來看一下MethodHandler的invoke方法的實(shí)現(xiàn)。

MethodHandler是個(gè)接口,有兩個(gè)實(shí)現(xiàn)類,一個(gè)是DefaultMethodHandler,這個(gè)是處理接口中的默認(rèn)方法的,另一個(gè)是SynchronousMethodHandler,這個(gè)是實(shí)現(xiàn)rpc調(diào)用的方法。接下來我們就看看SynchronousMethodHandler關(guān)于invoke方法的實(shí)現(xiàn)。

第一行通過方法的參數(shù)構(gòu)建了一個(gè)RequestTemplate,RequestTemplate可以看成是組裝http請求所需各種參數(shù)的封裝,比如什么情頭,body之類的都放在這里面。

第二行 Options options = findOptions(argv); 這個(gè)很有意思,Options主要是封裝了發(fā)送請求是連接超時(shí)時(shí)間和讀超時(shí)時(shí)間的配置,findOptions(argv)也就是先從參數(shù)里面找有沒有Options,沒有就返回構(gòu)造SynchronousMethodHandler的入?yún)r(shí)的Options,也就是說,連接超時(shí)時(shí)間和讀超時(shí)時(shí)間可以從方法入?yún)韨魅?,不過一般沒有人這么玩。

第三行就是搞一個(gè)重試的組件,是可以實(shí)現(xiàn)重試的,一般不設(shè)置。

然后執(zhí)行到executeAndDecode(template, options),進(jìn)入這個(gè)方法。

首先調(diào)用了targetRequest方法:

這個(gè)方法會遍歷所有的攔截器RequestInterceptor,這是feign的一個(gè)擴(kuò)展點(diǎn),也就說在發(fā)送請求前,仍然還有機(jī)會對請求的內(nèi)容進(jìn)行調(diào)整,比如說加個(gè)請求頭,這也是很常見的一種方式,在微服務(wù)之間鑒權(quán)的時(shí)候使用。RequestInterceptor是在構(gòu)建Feign.Builder的時(shí)候傳進(jìn)來的,F(xiàn)eign.Builder的組件都是通過ioc容器獲取的,組件又是通過配置類來的,所以你需要的話就可以在配置類中聲明RequestInterceptor對象。配置類有不同的優(yōu)先級,按照自己的需求,可以在其中一個(gè)優(yōu)先級使用,不過一般這種通用的東西,不是某個(gè)微服務(wù)特有的功能,一般選擇在springboot啟動中的容器中配置。

執(zhí)行完targetRequest,回到executeAndDecode之后,會構(gòu)建出一個(gè)Request,Request很好理解,就是一個(gè)請求,里面封裝了http請求的東西。接下來就會調(diào)用Client的execute方法來執(zhí)行請求,拿到響應(yīng),接下來就是基于處理這個(gè)響應(yīng),將響應(yīng)數(shù)據(jù)封裝成需要返回的參數(shù),之后返回給調(diào)用方。

到這里,我們已經(jīng)分析出接口的動態(tài)代理是如何運(yùn)行的。其實(shí)就是通過每個(gè)方法對應(yīng)的MethodHandler來實(shí)現(xiàn)的,MethodHandler主要就是拼接各種參數(shù),組裝成一個(gè)請求,隨后交由Client接口的實(shí)現(xiàn)去發(fā)送請求。

LoadBalancerFeignClient

通過上面分析整個(gè)動態(tài)代理調(diào)用過程可以看出,Client是發(fā)送http請求的關(guān)鍵類。那么Client是什么玩意?當(dāng)Feign客戶端在構(gòu)建動態(tài)代理的時(shí)候,填充很多組件到Feign.Builder中,其中有個(gè)組件就是Client的實(shí)現(xiàn),我們并沒有在FeignClientsConfiguration配置類中找到關(guān)于Client的對象的聲明。這個(gè)組件的實(shí)現(xiàn)是要依賴負(fù)載均衡的,也就是這個(gè)組件是Feign用來整合Ribbon的入口。

接下來,我們就著重看一下Client的實(shí)現(xiàn),看看Feign是如何通過ribbon實(shí)現(xiàn)負(fù)載均衡的。

我們先來看一下Feign跟ribbon整合的配置類。

我們來分析一下,首先通過@Impot注解導(dǎo)入了三個(gè)配置類。

  • HttpClientFeignLoadBalancedConfiguration:基于HttpClient實(shí)現(xiàn)http調(diào)用的。
  • OkHttpFeignLoadBalancedConfiguration:基于OkHttp實(shí)現(xiàn)http調(diào)用的。
  • DefaultFeignLoadBalancedConfiguration:默認(rèn)的,也就是Feign原生的發(fā)送http的實(shí)現(xiàn)。

這里我們看一下DefaultFeignLoadBalancedConfiguration配置類,因?yàn)槟J(rèn)就是它,HttpClientFeignLoadBalancedConfiguration和OkHttpFeignLoadBalancedConfiguration都需要有引入HttpClient和OkHttp依賴才會有用。

這個(gè)配置類很簡單,聲明了LoadBalancerFeignClient到spring容器,傳入了三個(gè)參數(shù),一個(gè)Client的實(shí)現(xiàn),一個(gè)
CachingSpringLoadBalancerFactory和一個(gè)SpringClientFactory。LoadBalancerFeignClient這個(gè)類實(shí)現(xiàn)了Client接口,也就數(shù)說我們在構(gòu)建Feign.Builder填充的就是這個(gè)對象,也就是上面說feign的執(zhí)行流程最后用來執(zhí)行請求的Client的實(shí)現(xiàn)。

接下來我說一下入?yún)⒌娜齻€(gè)參數(shù)是什么意思。

  • Client.Default:就是Feign自己實(shí)現(xiàn)的Client,里面封裝了真正發(fā)送http發(fā)送請求的功能,LoadBalancerFeignClient雖然也實(shí)現(xiàn)了Client接口,但是這個(gè)實(shí)現(xiàn)其實(shí)是為了整合Ribbon用的,并沒有發(fā)送http的功能,所以需要有個(gè)可以發(fā)送http功能的實(shí)現(xiàn)。
  • CachingSpringLoadBalancerFactory:后面會說這個(gè)類的作用
  • SpringClientFactory:這個(gè)跟Feign里面的FeignContext的作用差不多,用來實(shí)現(xiàn)配置隔離的,當(dāng)然,這個(gè)也在關(guān)于Ribbon的那篇文章有剖析過。
  • 其實(shí)大家可以自行去看OkHttpFeignLoadBalancedConfiguration和HttpClientFeignLoadBalancedConfiguration,其實(shí)他們配置跟DefaultFeignLoadBalancedConfiguration是一樣的,聲明的對象都是LoadBalancerFeignClient,只不過將Client.Default換成了基于HttpClient和OkHttp的實(shí)現(xiàn),也就是發(fā)送http請求使用的工具不一樣。

FeignRibbonClientAutoConfiguration除了導(dǎo)入配置類還聲明了CachingSpringLoadBalancerFactory,只不過一種是帶基于spring實(shí)現(xiàn)的重試功能的,一種是不帶的,主要看有沒有引入spring重試功能的包,所以上面構(gòu)建LoadBalancerFeignClient注入的CachingSpringLoadBalancerFactory就是在這聲明的。

這里就說完了Feign整合ribbon的配置類
FeignRibbonClientAutoConfiguration,我們也找到了構(gòu)造Feign.Builder的實(shí)現(xiàn)LoadBalancerFeignClient,接下來就來剖析LoadBalancerFeignClient的實(shí)現(xiàn)。

在動態(tài)代理調(diào)用的那里我們得出一個(gè)結(jié)論,那就是最后會調(diào)用Client接口的execute方法的實(shí)現(xiàn),所以我們就看一下execute方法的實(shí)現(xiàn),這里就是一堆操作,從請求的URL中拿到了clientName,也就是服務(wù)名。

為什么可以拿到服務(wù)名?

其實(shí)很簡單,OpenFeign構(gòu)建動態(tài)代理的時(shí)候,傳入了一個(gè)HardCodedTarget,當(dāng)時(shí)說在構(gòu)建HardCodedTarget的時(shí)候傳入了一個(gè)url,那個(gè)url當(dāng)時(shí)說了其實(shí)就是http://服務(wù)名,所以到這里,雖然有具體的請求接口的路徑,但是還是類似 http://服務(wù)名/api/sayHello這種,所以可以通過路徑拿到你鎖請求的服務(wù)名。

拿到服務(wù)名之后,再拿到了一個(gè)配置類IClientConfig,最后調(diào)用lbClient,我們看一下lbClient的方法實(shí)現(xiàn)。

就是調(diào)用CachingSpringLoadBalancerFactory的create方法。

這個(gè)方法先根據(jù)服務(wù)名從緩存中獲取一個(gè)FeignLoadBalancer,獲取不到就創(chuàng)建一個(gè)。

創(chuàng)建的過程就是從每個(gè)服務(wù)對應(yīng)的容器中獲取到IClientConfig和ILoadBalancer。Ribbon那篇文章都講過這些核心類,這里不再贅述。

默認(rèn)就是創(chuàng)建不帶spring重試功能的FeignLoadBalancer,放入緩存,最后返回這個(gè)FeignLoadBalancer。所以第一次來肯定沒有,需要構(gòu)建,也就是最終一定會返回FeignLoadBalancer,所以我們通過lbClient方法拿到的是FeignLoadBalancer。從這里可以看出
CachingSpringLoadBalancerFactory是構(gòu)建FeignLoadBalancer的工廠類,只不過先從緩存中查找,找不到再創(chuàng)建FeignLoadBalancer。

拿到FeignLoadBalancer之后就會調(diào)用executeWithLoadBalancer,接收到Response之后直接返回。

FeignLoadBalancer

那么這個(gè)FeignLoadBalancer又是啥呢?這里放上FeignLoadBalancer核心源碼。

FeignLoadBalancer繼承自AbstractLoadBalancerAwareClient,AbstractLoadBalancerAwareClient又是啥玩意?看過我寫的關(guān)于Ribbon核心組件已經(jīng)運(yùn)行原理的那篇文章小伙伴肯定知道,AbstractLoadBalancerAwareClient類主要作用是通過ILoadBalancer組件獲取一個(gè)Server,然后基于這個(gè)Server重構(gòu)了URI,也就是將你的請求路徑http://服務(wù)名/api/sayHello轉(zhuǎn)換成類似http://192.168.1.101:8088/api/sayHello這種路徑,也就是將原服務(wù)名替換成服務(wù)所在的某一臺機(jī)器ip和端口,替換之后就交由子類實(shí)現(xiàn)的exceut方法來發(fā)送http請求。

所以我們知道調(diào)用executeWithLoadBalancer之后,就會重構(gòu)請求路徑,將服務(wù)名替換成某個(gè)具體的服務(wù)器所在的ip和端口,之后交給子類execute來處理,對于這里來說,也就是FeignLoadBalancer的execute方法,因?yàn)镕eignLoadBalancer繼承
AbstractLoadBalancerAwareClient。

直接定位到execute方法最核心的一行代碼。

request.client()就會拿到構(gòu)建LoadBalancerFeignClient傳入的那個(gè)Client的實(shí)現(xiàn),我提到過,這個(gè)Client的實(shí)現(xiàn)是具體發(fā)送請求的實(shí)現(xiàn),默認(rèn)的就是Client.Default類(不是默認(rèn)就有可能是基于HttpClient或者是OkHttp的實(shí)現(xiàn))。所以這行代碼就是基于這個(gè)Client就成功的發(fā)送了Http請求,拿到響應(yīng),然后將這個(gè)Response 封裝成一個(gè)RibbonResponse返回,最后就返回給MethodHandler,然后解析響應(yīng),封裝成方法的返回值返回給調(diào)用者。

好了,其實(shí)到這里就完全知道Feign是如何整合Ribbon的,LoadBalancerFeignClient其實(shí)是OpenFeign適配Ribbon的入口,F(xiàn)eignLoadBalancer才是真正實(shí)現(xiàn)選擇負(fù)載均衡,發(fā)送http請求的組件,因?yàn)樗^承了
AbstractLoadBalancerAwareClient。

整個(gè)動態(tài)代理的調(diào)用過程:


通過這張圖,我們可以清楚地看出OpenFeign、Ribbon以及注冊中心之間的協(xié)同關(guān)系。

總結(jié)

OpenFeign在進(jìn)行rpc調(diào)用的時(shí)候,由于不知道服務(wù)具體在哪臺機(jī)器上,所以需要Ribbon這個(gè)負(fù)載均衡組件從服務(wù)所在的機(jī)器列表中選擇一個(gè),Ribbon中服務(wù)所在的機(jī)器列表是從注冊中心拉取的,Ribbon提供了一個(gè)ServerList接口,注冊中心實(shí)現(xiàn)之后,Ribbon就可以獲取到服務(wù)所在的機(jī)器列表,這就是這三個(gè)組件最基本的原理。

參考&鳴謝

https://www.cnblogs.com/zzyang/p/16404783.html

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2022-03-14 07:40:14

RibbonSpringNacos

2022-03-22 07:37:04

FeignSpringRibbon

2023-07-13 09:01:39

Bean接口容器

2023-08-29 17:51:22

Ribbon客戶端均衡器

2023-06-01 07:40:00

Ribbon負(fù)載均衡NacosRule

2023-10-27 17:23:03

Ribbon負(fù)載均衡策略

2010-04-25 16:55:38

實(shí)現(xiàn)負(fù)載均衡

2022-04-27 08:23:34

微服務(wù)負(fù)載均衡

2010-04-20 12:16:05

NAT負(fù)載均衡

2018-07-27 08:39:44

負(fù)載均衡算法實(shí)現(xiàn)

2010-11-19 12:53:53

梭子魚負(fù)載均衡

2009-11-09 12:45:44

路由負(fù)載均衡

2011-12-02 22:51:46

Nginx負(fù)載均衡

2023-11-08 07:45:47

Spring微服務(wù)

2020-01-14 09:40:00

Nginx負(fù)載均衡正向代理

2010-05-06 13:16:33

2021-04-30 08:19:32

SpringCloud客戶端負(fù)載Ribbo

2025-03-07 08:57:46

HTTP客戶端框架

2017-07-03 08:08:25

負(fù)載均衡分類

2011-05-26 16:18:51

Mongodb
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號