最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Cloud負(fù)載均衡組件Ribbon原理解析

 更新時(shí)間:2022年04月13日 11:42:29   作者:雙子孤狼  
本文主要講述了微服務(wù)體系下的?Spring?Cloud?Netflix?套件中?Ribbon?的使用,并結(jié)合部分源碼講述了?Ribbon?的底層原理,重點(diǎn)講述了?Ribbon?中是如何獲取服務(wù)以及如何判定一個(gè)服務(wù)是否可用,最后也介紹了?Ribbon?中默認(rèn)提供的?7?種負(fù)載均衡策略,感興趣的朋友一起看看吧

微服務(wù)體系下的 Spring Cloud Netflix 套件中 Ribbon 的主要用于負(fù)載均衡,底層默認(rèn)使用 RestTemplate 通訊,并提供了 7 種負(fù)載均衡策略

前言

在微服務(wù)中,對(duì)服務(wù)進(jìn)行拆分之后,必然會(huì)帶來(lái)微服務(wù)之間的通信需求,而每個(gè)微服務(wù)為了保證高可用性,又會(huì)去部署集群,那么面對(duì)一個(gè)集群微服務(wù)進(jìn)行通信的時(shí)候,如何進(jìn)行負(fù)載均衡也是必然需要考慮的問(wèn)題。那么有需求自然就有供給,由此一大批優(yōu)秀的開(kāi)源的負(fù)載均衡組件應(yīng)運(yùn)而生,本文就讓我們一起來(lái)分析一下 Spring Cloud Netflix 套件中的負(fù)載均衡組件 Ribbon。

一個(gè)問(wèn)題引發(fā)的思考

首先我們來(lái)看一個(gè)問(wèn)題,假如說(shuō)我們現(xiàn)在有兩個(gè)微服務(wù),一個(gè) user-center,一個(gè) user-order,我現(xiàn)在需要在 user-center 服務(wù)中調(diào)用 user-order 服務(wù)的一個(gè)接口。

這時(shí)候我們可以使用 HttpClient,RestTemplate 等發(fā)起 http 請(qǐng)求,user-center 服務(wù)端口為 8001,如下圖所示:

@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @GetMapping("/order")
    public String queryOrder(){
        return restTemplate.getForObject("http://localhost:8002/order/query",String.class);
    }
}

user-order 服務(wù)中只是簡(jiǎn)單的定義了一個(gè)接口,user-order 服務(wù)端口為 8002

@RestController
@RequestMapping(value = "/order")
public class UserOrderController {

    @GetMapping(value = "/query")
    public String queryAllOrder(){
        return "all orders";
    }
}

這時(shí)候只需要將兩個(gè)服務(wù)啟動(dòng),訪問(wèn) http://localhost:8001/user/order 就可以獲取到所有的訂單信息。

可以看到,這樣是可以在兩個(gè)微服務(wù)之間進(jìn)行通訊的,但是,假如說(shuō)我們的 user-order 服務(wù)是一個(gè)集群呢?這時(shí)候怎么訪問(wèn)呢?因?yàn)?user-order 服務(wù)已經(jīng)是集群,所以必然需要一種算法來(lái)決定應(yīng)該請(qǐng)求到哪個(gè) user-order 服務(wù)中,最簡(jiǎn)單的那么自然就是隨機(jī)或者輪詢機(jī)制,輪詢或者隨機(jī)其實(shí)就是簡(jiǎn)單的負(fù)載均衡算法,而 Ribbon 就是用來(lái)實(shí)現(xiàn)負(fù)載均衡的一個(gè)組件,其內(nèi)部支持輪詢,等算法。

Ribbon的簡(jiǎn)單使用

接下來(lái)我們看看 Ribbon 的簡(jiǎn)單使用。

首先改造 user-order 服務(wù),在 user-order 服務(wù)中定義一個(gè)服務(wù)名配置:

spring.application.name=user-order-service

user-order 服務(wù)中的 UserOrderController 稍微改造一下,新增一個(gè)端口的輸出來(lái)區(qū)分:

@RestController
@RequestMapping(value = "/order")
public class UserOrderController {

    @Value("${server.port}")
    private int serverPort;

    @GetMapping(value = "/query")
    public String queryAllOrder(){
        return "訂單來(lái)自:" + serverPort;
    }
}
  • 通過(guò) VM 參數(shù) -Dserver.port=8002-Dserver.port=8003 分別來(lái)啟動(dòng)兩個(gè) user-order 服務(wù)。
  • 接下來(lái)改造 user-center 服務(wù),在 user-center 服務(wù)中引入 Ribbon 的相關(guān)依賴:
 <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      <version>2.2.3.RELEASE</version>
    </dependency>
  • user-center 服務(wù)中新增一個(gè) Ribbon 相關(guān)配置,列舉出需要訪問(wèn)的所有服務(wù):
user-order-service.ribbon.listOfServers=\
  localhost:8002,localhost:8003
  • 對(duì) user-center 服務(wù)中的 UserController 進(jìn)行改造:
@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @GetMapping("/order")
    public String queryOrder(){
        //獲取一個(gè) user-order 服務(wù)
        ServiceInstance serviceInstance = loadBalancerClient.choose("user-order-service");
        String url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort()) + "/order/query";
        return restTemplate.getForObject(url,String.class);
    }
}

這時(shí)候我們?cè)俅卧L問(wèn) http://localhost:8001/user/order 就可以看到請(qǐng)求的 user-order 服務(wù)會(huì)在 80028003 之間進(jìn)行切換。

Ribbon 原理分析

看了上面 Ribbon 的使用示例,會(huì)不會(huì)覺(jué)得有點(diǎn)麻煩,每次還需要自己去獲取 ip 和端口,然后格式化 url,但是其實(shí)實(shí)際開(kāi)發(fā)過(guò)程中我們并不會(huì)通過(guò)這么原始的方式來(lái)編寫代碼,接下來(lái)我們?cè)賹?duì)上面的示例進(jìn)行一番改造:

@RestController
@RequestMapping(value = "/user")
public class UserController3 {
    @Autowired
    private RestTemplate restTemplate;

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @GetMapping("/order")
    public String queryOrder(){
        return restTemplate.getForObject("http://user-order-service/order/query",String.class);
    }
}

在這個(gè)示例中,主要就是一個(gè)關(guān)鍵主鍵起了作用:@LoadBalanced。

@LoadBalanced 注解

進(jìn)入 @LoadBalanced 注解中,我們可以看到,這個(gè)注解其實(shí)沒(méi)有任何邏輯,只是加了一個(gè) @Qualifier 注解:

這個(gè)注解大家應(yīng)該很熟悉了,常用語(yǔ)同一個(gè) Bean 有多個(gè)不同名稱注入的場(chǎng)景。

@Qualifier注解

下面我們通過(guò)一個(gè)例子來(lái)演示一下 Qualifier注解的用法。

新建一個(gè)空的 TestDemo 類,并新增一個(gè) TestConfiguration 類來(lái)創(chuàng)建不同名稱的 TestDemo

@Configuration
public class TestConfiguration {
    @Bean("testDemo1")
    public TestDemo testDemo(){
        return new TestDemo();
    }

    @Bean("testDemo2")
    public TestDemo testDemo2(){
        return new TestDemo();
    }
}

這時(shí)候我們?nèi)绻枰⑷?TestDemo,那么有很多種辦法,具體的使用就需要看業(yè)務(wù)需要來(lái)決定。

  • 方法一:直接使用 @Autowired,并使用 List 集合來(lái)接收 Bean,這樣所有 TestDemo 類型的 Bean 都會(huì)被注入。
  • 方法二:通過(guò)使用 @Resource(name = "testDemo1") 注解來(lái)指定名稱,這樣就可以只注入一個(gè) Bean。
  • 方法三:通過(guò)使用 @Resource@Qualifier(value = "testDemo1") 來(lái)指定一個(gè) Bean,其實(shí)這種方式和方法二的效果基本一致。
  • 方法四:使用 @Autowired@Qualifier 注解來(lái)注入,不指定任何名稱,如下所示:
@Configuration
public class TestConfiguration {
    @Bean("testDemo1")
    public TestDemo testDemo(){
        return new TestDemo();
    }

    @Bean("testDemo2")
    public TestDemo testDemo2(){
        return new TestDemo();
    }
}

這時(shí)候運(yùn)行之后我們發(fā)現(xiàn)不會(huì)有任何 Bean 被注入到集合中,這是因?yàn)楫?dāng)使用這種方式來(lái)注入時(shí),Spring 會(huì)認(rèn)為當(dāng)前只需要注入被 @Qualifier 注解標(biāo)記的 Bean,而我們上面定義的兩個(gè) TestDemo 都沒(méi)有被 @Qualifier 修飾。

這時(shí)候,我們只需要在 TestConfiguration 稍微改造,在 TestDemo 的定義上加上 @Qualifier 修飾即可:

@Configuration
public class TestConfiguration {

    @Bean("testDemo1")
    @Qualifier
    public TestDemo testDemo(){
        return new TestDemo();
    }

    @Bean("testDemo2")
    @Qualifier
    public TestDemo testDemo2(){
        return new TestDemo();
    }
}

這時(shí)候再去運(yùn)行,就會(huì)發(fā)現(xiàn),testDemo1testDemo2 都會(huì)被注入。

LoadBalancerAutoConfiguration 自動(dòng)裝配

SpringCloud 是基于 SpringBoot 實(shí)現(xiàn)的,所以我們常用的這些分布式組件都會(huì)基于 SpringBoot 自動(dòng)裝配來(lái)實(shí)現(xiàn),我們進(jìn)入 LoadBalancerAutoConfiguration 自動(dòng)裝配類可以看到,RestTemplate 的注入加上了 @LoadBalanced,這就是為什么我們前面的例子中加上了 @LoadBalanced 就能被自動(dòng)注入的原因:

RestTemplateCustomizer

上面我們看到,RestTemplate 被包裝成為了 RestTemplateCustomizer,而 RestTemplateCustomizer 的注入如下:

可以看到這里面加入了一個(gè)攔截器 LoadBalancerInterceptor,事實(shí)上即使不看這里,我們也可以猜測(cè)到,我們直接使用服務(wù)名就可以進(jìn)行通訊的原因必然是底層有攔截器對(duì)其進(jìn)行轉(zhuǎn)換成 ip 形式,并在底層進(jìn)行負(fù)載均衡選擇合適的服務(wù)進(jìn)行通訊。

LoadBalancerInterceptor

LoadBalancerInterceptorRibbon 中默認(rèn)的一個(gè)攔截器,所以當(dāng)我們調(diào)用 RestTemplategetObject 方法時(shí),必然會(huì)調(diào)用攔截器中的方法。

從源碼中可以看到,LoadBalancerInterceptor 中只有一個(gè) intercept() 方法:

RibbonLoadBalancerClient#execute

繼續(xù)跟進(jìn) execute 方法會(huì)進(jìn)入到 RibbonLoadBalancerClient 類(由 RibbonAutoConfiguration 自動(dòng)裝配類初始化)中:

這個(gè)方法中也比較好理解,首先獲取一個(gè)負(fù)載均衡器,然后再通過(guò) getServer 方法獲取一個(gè)指定的服務(wù),也就是當(dāng)我們有多個(gè)服務(wù)時(shí),到這里就會(huì)選出一個(gè)服務(wù)進(jìn)行通訊。

進(jìn)入 getServer 方法:

我們看到,最終會(huì)調(diào)用 ILoadBalancer 中的 chooseServer 方法,而 ILoadBalancer 是一個(gè)頂層接口,這時(shí)候具體會(huì)調(diào)用哪個(gè)實(shí)現(xiàn)類那么就需要先來(lái)看一下類圖:

這里直接看類圖也無(wú)法看出到底會(huì)調(diào)用哪一個(gè),但是不論調(diào)用哪一個(gè),我們猜測(cè)他肯定會(huì)有一個(gè)地方去初始化這個(gè)類,而在 Spring 當(dāng)中一般就是自動(dòng)裝配類中初始化或者 Configuration 中初始化,而 ILoadBalancer 正是在 RibbonClientConfiguration 類中被加載的:

ZoneAwareLoadBalancer 負(fù)載均衡器

ZoneAwareLoadBalancer 的初始化會(huì)調(diào)用其父類 DynamicServerListLoadBalancer 進(jìn)行初始化,然后會(huì)調(diào)用 restOfInit 方法進(jìn)行所有服務(wù)的初始化。

如何獲取所有服務(wù)

使用 Ribbon 后,我們通訊時(shí)并沒(méi)有指定某一個(gè) ip 和端口,而是通過(guò)服務(wù)名來(lái)調(diào)用服務(wù),那么這個(gè)服務(wù)名就可能對(duì)應(yīng)多個(gè)真正的服務(wù),那么我們就必然需要先獲取到所有服務(wù)的 ip 和端口等信息,然后才能進(jìn)行負(fù)載均衡處理。

獲取所有服務(wù)有兩種方式:

  • 從配置文件獲取
  • Eureka 注冊(cè)中心獲?。ㄐ枰胱?cè)中心)。

初始化服務(wù)的方式是通過(guò)啟動(dòng)一個(gè) Scheduled 定時(shí)任務(wù)來(lái)實(shí)現(xiàn)的,默認(rèn)就是 30s 更新一次,其實(shí)在很多源碼中都是通過(guò)這種方式來(lái)定時(shí)更新的,因?yàn)樵创a要考慮的使用的簡(jiǎn)單性所以不太可能引入一個(gè)第三方中間件來(lái)實(shí)現(xiàn)定時(shí)器。

具體的源碼如下所示:enableAndInitLearnNewServersFeature() 方法啟動(dòng)的定時(shí)任務(wù)最終仍然你是調(diào)用 updateListOfServers() 方法來(lái)更新服務(wù)。

最終在獲取到服務(wù)之后會(huì)調(diào)用父類 BaseLoadBalancer 中的將所有服務(wù)設(shè)置到 allServerList 集合中(BaseLoadBalancer 類中維護(hù)了一些負(fù)載均衡需要使用到的服務(wù)相關(guān)信息)。

如何判斷服務(wù)是否可用

當(dāng)我們獲取到配置文件(或者 Eureka 注冊(cè)中心)中的所有服務(wù),那么這時(shí)候能直接執(zhí)行負(fù)載均衡策略進(jìn)行服務(wù)分發(fā)嗎?顯然是不能的,因?yàn)橐呀?jīng)配置好的服務(wù)可能會(huì)宕機(jī)(下線),從而導(dǎo)致服務(wù)不可用,所以在 BaseLoadBalancer 中除了有一個(gè) allServerList 集合來(lái)維護(hù)所有服務(wù)器,還有一個(gè)集合 upServerList 用來(lái)維護(hù)可用服務(wù)集合,那么如何判斷一個(gè)服務(wù)是否可用呢?答案就是通過(guò)心跳檢測(cè)來(lái)判斷一個(gè)服務(wù)是否可用。

心跳檢測(cè) Task

在講心跳檢測(cè)之前,我們先看一下 BaseLoadBalancer 中的 setServersList 方法,有一段邏輯比較重要:

這段邏輯我們看到,默認(rèn)情況下,如果 Ping 的策略是 DummyPing,那么默認(rèn) upServerList = allServerList,而實(shí)際上,假如我們沒(méi)有進(jìn)行進(jìn)行特殊配置,其實(shí)默認(rèn)的就是 DummyPing,這也是在 RibbonClientConfiguration 類中被加載的:

BaseLoadBalancer 初始化過(guò)程中,也會(huì)啟動(dòng)一個(gè) Scheduled 定時(shí)任務(wù)去定時(shí)更新任務(wù),最終和 forceQuickPing() 方法一樣,調(diào)用一個(gè)默認(rèn)策略來(lái)觸發(fā)心跳檢測(cè),而默認(rèn)策略就是 DummyPing,也就是默認(rèn)所有服務(wù)都是可用的。

雖然默認(rèn)不執(zhí)行真正的心跳檢測(cè)操作,但是 Netflix 中提供了 PingUrl 等其他策略,PingUrl 其實(shí)就是發(fā)起一個(gè) http 請(qǐng)求,如果有響應(yīng)就認(rèn)為服務(wù)可用,沒(méi)響應(yīng)就認(rèn)為服務(wù)不可用。

修改心跳檢測(cè)策略可以通過(guò)如下配置切換(user-order-service 為客戶端的服務(wù)名),既然是可配置的,那么也可以自己實(shí)現(xiàn)一個(gè)策略,只需要實(shí)現(xiàn) IPing 接口即可。

user-order-service.ribbon.NFLoadBalancerPingClassName=com.netflix.loadbalancer.PingUrl

Ribbon 的負(fù)載均衡算法

當(dāng)獲取到可用服務(wù)之后,那么最后應(yīng)該選擇哪一個(gè)服務(wù)呢?這就需要使用到負(fù)載均衡策略,在 Ribbon 中,可以通過(guò)配置修改,也可以自定義負(fù)載均衡策略(實(shí)現(xiàn) IRule 接口)。

  • RandomRule:隨機(jī)算法
  • RoundRobinRule:輪詢算法
  • ZoneAvoidanceRule:結(jié)合分區(qū)統(tǒng)計(jì)信息篩選出合適的分區(qū)(默認(rèn)的負(fù)載均衡算法)
  • RetryRule:在 deadline 時(shí)間內(nèi),如果請(qǐng)求不成功,則重新發(fā)起請(qǐng)求知道找到一個(gè)可用的服務(wù)。
  • WeightedResponseTimeRule:根據(jù)服務(wù)器的響應(yīng)時(shí)間計(jì)算權(quán)重值,服務(wù)器響應(yīng)時(shí)間越長(zhǎng),這個(gè)服務(wù)器的權(quán)重就越小,會(huì)有定時(shí)任務(wù)對(duì)權(quán)重值進(jìn)行更新。
  • AvailabilityFilteringRule:過(guò)濾掉短路(連續(xù) 3 次連接失?。┑姆?wù)和高并發(fā)的服務(wù)。
  • BestAvailableRule:選擇并發(fā)數(shù)最低的服務(wù)器

負(fù)載均衡算法可通過(guò)以下配置進(jìn)行修改:

user-order-service.ribbon.NFLoadBalancerRuleClassName=Rule規(guī)則的類名

總結(jié)

本文主要講述了微服務(wù)體系下的 Spring Cloud Netflix 套件中 Ribbon 的使用,并結(jié)合部分源碼講述了 Ribbon 的底層原理,重點(diǎn)講述了 Ribbon 中是如何獲取服務(wù)以及如何判定一個(gè)服務(wù)是否可用,最后也介紹了 Ribbon 中默認(rèn)提供的 7 種負(fù)載均衡策略。

到此這篇關(guān)于Spring Cloud之負(fù)載均衡組件Ribbon原理分析的文章就介紹到這了,更多相關(guān)Spring Cloud負(fù)載均衡組件Ribbon內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java通過(guò)python命令執(zhí)行DataX任務(wù)的實(shí)例

    Java通過(guò)python命令執(zhí)行DataX任務(wù)的實(shí)例

    今天小編就為大家分享一篇Java通過(guò)python命令執(zhí)行DataX任務(wù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-08-08
  • 使用Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的示例詳解

    使用Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的示例詳解

    在實(shí)際的開(kāi)發(fā)過(guò)程中,我們常常會(huì)遇到需要將 Excel 文件轉(zhuǎn)換為 PDF 文件的需求,本文為大家介紹一種Java中的常見(jiàn)實(shí)現(xiàn)方式,需要的可以參考一下
    2025-02-02
  • IntelliJ IDEA多屏后窗口不顯示問(wèn)題解決方案

    IntelliJ IDEA多屏后窗口不顯示問(wèn)題解決方案

    這篇文章主要介紹了IntelliJ IDEA多屏后窗口不顯示問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Springboot+Poi導(dǎo)入Excel表格實(shí)現(xiàn)過(guò)程詳解

    Springboot+Poi導(dǎo)入Excel表格實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了Springboot+Poi導(dǎo)入Excel表格實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • java檢查數(shù)組是否有重復(fù)元素的方法

    java檢查數(shù)組是否有重復(fù)元素的方法

    這篇文章主要介紹了java檢查數(shù)組是否有重復(fù)元素的方法,涉及java針對(duì)數(shù)組元素的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • Spring的IOC原理詳情

    Spring的IOC原理詳情

    這篇文章主要介紹了Spring的IOC原理詳情,IOC是Inversion?of?Control的縮寫,多數(shù)書籍翻譯成“控制反轉(zhuǎn)”,還有些書籍翻譯成為控制反向或者控制倒置
    2022-07-07
  • springsecurity實(shí)現(xiàn)攔截器的使用示例

    springsecurity實(shí)現(xiàn)攔截器的使用示例

    Spring Security 可以替代攔截器,同時(shí)還可以提供更加細(xì)粒度的權(quán)限控制和身份認(rèn)證,本文就來(lái)介紹一下springsecurity實(shí)現(xiàn)攔截器的使用示例,感興趣的可以了解一下
    2023-10-10
  • Struts和servlet不能共存問(wèn)題解決方法

    Struts和servlet不能共存問(wèn)題解決方法

    這篇文章主要介紹了Struts和servlet不能共存問(wèn)題解決方法,共有三種方法,需要的朋友可以參考下。
    2017-09-09
  • Gson中的TypeToken與泛型擦除詳情

    Gson中的TypeToken與泛型擦除詳情

    這篇文章主要介紹了Gson中的TypeToken與泛型擦除詳情,其Gson類提供了toJson()與fromJson()方法,分別用來(lái)序列化與反序列化,更多相關(guān)內(nèi)容需要的朋友可以參考一下
    2022-09-09
  • Redisson分布式鎖的源碼解讀分享

    Redisson分布式鎖的源碼解讀分享

    Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory?Data?Grid)。Redisson有一樣功能是可重入的分布式鎖。本文來(lái)討論一下這個(gè)功能的特點(diǎn)以及源碼分析
    2022-11-11

最新評(píng)論

崇仁县| 马关县| 峨山| 积石山| 嘉祥县| 方山县| 和平区| 龙山县| 海淀区| 宾川县| 高密市| 莒南县| 襄城县| 通州区| 玉溪市| 济阳县| 天气| 崇左市| 龙门县| 和政县| 新邵县| 车险| 渝中区| 和林格尔县| 灵台县| 清流县| 达州市| 武清区| 南陵县| 丹巴县| 苗栗县| 杭州市| 巴南区| 贵州省| 崇州市| 渭源县| 临漳县| 哈尔滨市| 香格里拉县| 仙居县| 黄石市|