溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Ribbon如何使用

發(fā)布時間:2022-01-12 09:05:28 來源:億速云 閱讀:112 作者:iii 欄目:云計算

這篇“Ribbon如何使用”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Ribbon如何使用”文章吧。

Ribbon基本使用

簡介

Ribbon是一個客戶端負(fù)載均衡工具,封裝Netflix Ribbon組件,能夠提供客戶端負(fù)載均衡能力。

理解Ribbon最重要的就是理解客戶端這個概念,所謂客戶端負(fù)載均衡工具不同于Nginx(服務(wù)端負(fù)載均衡),Ribbon和應(yīng)用程序綁定,本身不是獨立的服務(wù),也不存儲服務(wù)列表,需要負(fù)載均衡的時候,會通過應(yīng)用程序獲取注冊服務(wù)列表,然后通過列表進(jìn)行負(fù)載均衡和調(diào)用。

  • Nginx獨立進(jìn)程做負(fù)載均衡,通過負(fù)載均衡策略,將請求轉(zhuǎn)發(fā)到不同的服務(wù)上

  • 客戶端負(fù)載均衡,通過在客戶端保存服務(wù)列表信息,然后自己調(diào)用負(fù)載均衡策略,分?jǐn)傉{(diào)用不同的服務(wù)

基本使用

Ribbon的負(fù)載均衡有兩種方式

  1. 和 RestTemplate 結(jié)合 Ribbon+RestTemplate

  2. 和 OpenFeign 結(jié)合

Ribbon的核心子模塊

  1. ribbon-loadbalancer:可以獨立使用或者和其他模塊一起使用的負(fù)載均衡API

  2. ribbon-core:Ribbon的核心API

訂單服務(wù)集成Ribbon

訂單服務(wù)調(diào)用商品服務(wù)

配置過程 分兩步

  1. 在訂單服務(wù)中導(dǎo)入ribbon的依賴

    <!--ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>


  2. 配置 RestTemplate

    Ribbon如何使用

訂單服務(wù)調(diào)用商品服務(wù)

  1. 訂單服務(wù)調(diào)用商品服務(wù)的鏈接 不能寫成ip+端口號,需要寫成商品服務(wù)的服務(wù)名稱

    Ribbon如何使用

  2. 重啟 訂單服務(wù) 測試負(fù)載均衡

    Ribbon如何使用

    Ribbon如何使用

Ribbon負(fù)載均衡簡單版實現(xiàn)的流程

  1. RestTemplate發(fā)送的請求是服務(wù)名稱http://nacos-product/product/getProductById/1

  2. 獲取@LoadBalanced注解標(biāo)記的RestTemplate

  3. RestTemplate添加一個攔截器,當(dāng)使用RestTemplate發(fā)起http調(diào)用時進(jìn)行攔截

  4. 根據(jù)url中的服務(wù)名稱 以及自身的負(fù)載均衡策略 去訂單服務(wù)的服務(wù)列表中找到一個要調(diào)用的ip+端口號 localhost:8802

  5. 訪問該目標(biāo)服務(wù),并獲取返回結(jié)果

Ribbon如何使用

服務(wù)列表實際上是個map

Ribbon如何使用

Ribbon負(fù)載均衡原理 [了解]

獲取@LoadBalanced注解標(biāo)記的RestTemplate。

Ribbon將所有標(biāo)記@LoadBalanced注解的RestTemplate保存到一個List集合當(dāng)中,具體源碼如下:

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();

具體源碼位置是在LoadBalancerAutoConfiguration中。

RestTemplate添加一個攔截器

攔截器不是Ribbon的功能

RestTemplate添加攔截器需要有兩個步驟,首先是定義一個攔截器,其次是將定義的攔截器添加到RestTemplate中。

定義一個攔截器

實現(xiàn)ClientHttpRequestInterceptor接口就具備了攔截請求的功能,該接口源碼如下:

public interface ClientHttpRequestInterceptor {
    /**
     *實現(xiàn)該方法,在該方法內(nèi)完成攔截請求后的邏輯內(nèi)容。
     *對于ribbon而言,在該方法內(nèi)完成了根據(jù)具體規(guī)則從
     *服務(wù)集群中選取一個服務(wù),并向該服務(wù)發(fā)起請求的操作。
     */
   ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;

}

ribbon中對應(yīng)的實現(xiàn)類是LoadBalancerInterceptor具體源碼如下:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

   private LoadBalancerClient loadBalancer;
   private LoadBalancerRequestFactory requestFactory;

    //省略構(gòu)造器代碼...

   @Override
   public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
         final ClientHttpRequestExecution execution) throws IOException {
      final URI originalUri = request.getURI();
      String serviceName = originalUri.getHost();
      /**
       *攔截請求,并調(diào)用loadBalancer.execute()方法
       *在該方法內(nèi)部完成server的選取。向選取的server
       *發(fā)起請求,并獲得返回結(jié)果。
       */
      return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
   }
}
將攔截器添加到RestTemplate中

RestTemplate繼承了InterceptingHttpAccessor,在InterceptingHttpAccessor中提供了獲取以及添加攔截器的方法,具體源碼如下:

public abstract class InterceptingHttpAccessor extends HttpAccessor {

    /**
     * 所有的攔截器是以一個List集合形式進(jìn)行保存。
     */
   private List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();

   /**
    * 設(shè)置攔截器。
    */
   public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
      this.interceptors = interceptors;
   }

   /**
    * 獲取當(dāng)前的攔截器。
    */
   public List<ClientHttpRequestInterceptor> getInterceptors() {
      return interceptors;
   }

   //省略部分代碼...
}

通過這兩個方法我們就可以將剛才定義的LoadBalancerInterceptor添加到有@LoadBalanced注解標(biāo)識的RestTemplate中。具體的源碼如下(LoadBalancerAutoConfiguration)省略部分代碼:

public class LoadBalancerAutoConfiguration {

    /**
      * 獲取所有帶有@LoadBalanced注解的restTemplate
     */
   @LoadBalanced
   @Autowired(required = false)
   private List<RestTemplate> restTemplates = Collections.emptyList();

    /**
     * 創(chuàng)建SmartInitializingSingleton接口的實現(xiàn)類。Spring會在所有
     * 單例Bean初始化完成后回調(diào)該實現(xiàn)類的afterSingletonsInstantiated()
     * 方法。在這個方法中會為所有被@LoadBalanced注解標(biāo)識的
     * RestTemplate添加ribbon的自定義攔截器LoadBalancerInterceptor。
     */
   @Bean
   public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
         final List<RestTemplateCustomizer> customizers) {
      return new SmartInitializingSingleton() {
         @Override
         public void afterSingletonsInstantiated() {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
               for (RestTemplateCustomizer customizer : customizers) {
                  customizer.customize(restTemplate);
               }
            }
         }
      };
   }
    /**
     * 創(chuàng)建Ribbon自定義攔截器LoadBalancerInterceptor
     * 創(chuàng)建前提是當(dāng)前classpath下不存在spring-retry。
     * 所以LoadBalancerInterceptor是默認(rèn)的Ribbon攔截
     * 請求的攔截器。
     */
    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
   static class LoadBalancerInterceptorConfig {
      @Bean
      public LoadBalancerInterceptor ribbonInterceptor(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }

      /**
       * 添加攔截器具體方法。首先獲取當(dāng)前攔截器集合(List)
       * 然后將loadBalancerInterceptor添加到當(dāng)前集合中
       * 最后將新的集合放回到restTemplate中。
       */
      @Bean
      @ConditionalOnMissingBean
      public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
         return new RestTemplateCustomizer() {
            @Override
            public void customize(RestTemplate restTemplate) {
               List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                     restTemplate.getInterceptors());
               list.add(loadBalancerInterceptor);
               restTemplate.setInterceptors(list);
            }
         };
      }
   }
}

至此知道了ribbon攔截請求的基本原理,接下來我們看看Ribbon是怎樣選取server的。

Ribbon選取server原理概覽

通過上面的介紹我們知道了當(dāng)發(fā)起請求時ribbon會用LoadBalancerInterceptor這個攔截器進(jìn)行攔截。在該攔截器中會調(diào)用LoadBalancerClient.execute()方法,該方法具體代碼如下:

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  /**
   *創(chuàng)建loadBalancer的過程可以理解為組裝選取服務(wù)的規(guī)則(IRule)、
   *服務(wù)集群的列表(ServerList)、檢驗服務(wù)是否存活(IPing)等特性
   *的過程(加載RibbonClientConfiguration這個配置類),需要注意
   *的是這個過程并不是在啟動時進(jìn)行的,而是當(dāng)有請求到來時才會處理。
   */
   ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

   /**
    * 根據(jù)ILoadBalancer來選取具體的一個Server。
    * 選取的過程是根據(jù)IRule、IPing、ServerList
    * 作為參照。
    */
   Server server = getServer(loadBalancer);
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }
   RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
         serviceId), serverIntrospector(serviceId).getMetadata(server));

   return execute(serviceId, ribbonServer, request);
}

通過代碼我們可知,首先創(chuàng)建一個ILoadBalancer,這個ILoadBalancer是Ribbon的核心類??梢岳斫獬伤诉x取服務(wù)的規(guī)則(IRule)、服務(wù)集群的列表(ServerList)、檢驗服務(wù)是否存活(IPing)等特性,同時它也具有了根據(jù)這些特性從服務(wù)集群中選取具體一個服務(wù)的能力。 Server server = getServer(loadBalancer);這行代碼就是選取舉一個具體server。 最終調(diào)用了內(nèi)部的execute方法,該方法代碼如下(只保留了核心代碼):

@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
   try {
      //發(fā)起調(diào)用
      T returnVal = request.apply(serviceInstance);
      statsRecorder.recordStats(returnVal);
      return returnVal;
   }
   catch (IOException ex) {
      statsRecorder.recordStats(ex);
      throw ex;
   }
   catch (Exception ex) {
      statsRecorder.recordStats(ex);
      ReflectionUtils.rethrowRuntimeException(ex);
   }
   return null;
}

接下來看下request.apply(serviceInstance)方法的具體做了那些事情(LoadBalancerRequestFactory中):

@Override
public ClientHttpResponse apply(final ServiceInstance instance)
      throws Exception {
   HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
   //省略部分代碼...
   /**
    * 發(fā)起真正請求。
    */
   return execution.execute(serviceRequest, body);
}

看到這里整體流程的原理就說完了,接下來我們結(jié)合一張圖來回顧下整個過程:

Ribbon如何使用

首先獲取所有標(biāo)識@LoadBalanced注解的RestTemplate(可以理解成獲取那些開啟了Ribbon負(fù)載均衡功能的RestTemplate),然后將Ribbon默認(rèn)的攔截器LoadBalancerInterceptor添加到RestTemplate中,這樣當(dāng)使用RestTemplate發(fā)起http請求時就會起到攔截的作用。當(dāng)有請求發(fā)起時,ribbon默認(rèn)的攔截器首先會創(chuàng)建ILoadBalancer(里面包含了選取服務(wù)的規(guī)則(IRule)、服務(wù)集群的列表(ServerList)、檢驗服務(wù)是否存活(IPing)等特性)。在代碼層面的含義是加載RibbonClientConfiguration配置類)。然后使用ILoadBalancer從服務(wù)集群中選擇一個服務(wù),最后向這個服務(wù)發(fā)送請求。

Ribbon負(fù)載均衡規(guī)則

參考資料:https://www.jianshu.com/p/79b9cf0d0519

Ribbon默認(rèn)負(fù)載均衡規(guī)則

根據(jù)上述Ribbon的原理,可以知道IRule接口負(fù)責(zé)負(fù)載均衡的實現(xiàn),具體如下:

Ribbon如何使用

規(guī)則名稱特點
AvailabilityFilteringRule過濾掉一直連接失敗的被標(biāo)記為circuit tripped的后端Server,并 過濾掉那些高并發(fā)的后端Server或者使用一個AvailabilityPredicate 來包含過濾server的邏輯,其實就是檢查status里記錄的各個server 的運行狀態(tài)
BestAvailableRule選擇一個最小的并發(fā)請求的server,逐個考察server, 如果Server被tripped了,則跳過
RandomRule隨機選擇一個Server
ResponseTimeWeightedRule已廢棄,作用同WeightedResponseTimeRule
WeightedResponseTimeRule權(quán)重根據(jù)響應(yīng)時間加權(quán),響應(yīng)時間越長,權(quán)重越小,被選中的可能性越低
RetryRule對選定的負(fù)載均衡策略加上重試機制,在一個配置時間段內(nèi)當(dāng) 選擇Server不成功,則一直嘗試使用subRule的方式選擇一個 可用的Server
RoundRobinRule輪詢選擇,輪詢index,選擇index對應(yīng)位置的Server
ZoneAvoidanceRule默認(rèn)的負(fù)載均衡策略,即復(fù)合判斷Server所在區(qū)域的性能和Server的可用性 選擇Server,在沒有區(qū)域的環(huán)境下,類似于輪詢(RandomRule)

其中RandomRule表示隨機策略、RoundRobinRule表示輪詢策略、WeightedResponseTimeRule表示加權(quán)策略、BestAvailableRule表示請求數(shù)最少策略等等

隨機源碼:

Ribbon如何使用

輪詢源碼:

Ribbon如何使用

修改默認(rèn)的自定義規(guī)則

默認(rèn)是輪詢 可以修改為任意的規(guī)則

修改為隨機算法

  1. 創(chuàng)建具有負(fù)載均衡功能的RestTemplate實例

    [@Bean](https://my.oschina.net/bean)
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }


    使用RestTemplate進(jìn)行rest操作的時候,會自動使用負(fù)載均衡策略,它內(nèi)部會在RestTemplate中加入LoadBalancerInterceptor這個攔截器,這個攔截器的作用就是使用負(fù)載均衡。

    默認(rèn)情況下會采用輪詢策略,如果希望采用其它策略,則指定IRule實現(xiàn),如:

    [@Bean](https://my.oschina.net/bean)
    public IRule ribbonRule() {
        return new BestAvailableRule();
    }


    這種方式對OpenFeign也有效。

修改為按照Nacos配置的權(quán)重進(jìn)行負(fù)載均衡

  1. 在nacos中對集群進(jìn)行權(quán)重的配置

    Ribbon如何使用

    Ribbon如何使用

    Ribbon如何使用

  2. 在項目中,選擇使用 NacosRule

    Ribbon如何使用

Ribbon實戰(zhàn)優(yōu)化

饑餓加載

Ribbon默認(rèn)懶加載,意味著只有在發(fā)起調(diào)用的時候才會創(chuàng)建客戶端

ribbon:
  eager-load:
    # 開啟ribbon饑餓加載
    enabled: true
    # 配置user-center使用ribbon饑餓加載,多個使用逗號分隔
    clients: user-center

參數(shù)調(diào)優(yōu)

主要調(diào)整請求的超時時間,是否重試

如果業(yè)務(wù)沒有做冪等性的話建議把重試關(guān)掉:ribbon.MaxAutoRetriesNextServer=0

# 從注冊中心刷新servelist的時間 默認(rèn)30秒,單位ms
ribbon.ServerListRefreshInterval=15000
# 請求連接的超時時間 默認(rèn)1秒,單位ms
ribbon.ConnectTimeout=30000
# 請求處理的超時時間 默認(rèn)1秒,單位ms
ribbon.ReadTimeout=30000
# 對所有操作請求都進(jìn)行重試,不配置這個MaxAutoRetries不起作用 默認(rèn)false
#ribbon.OkToRetryOnAllOperations=true
# 對當(dāng)前實例的重試次數(shù) 默認(rèn)0
# ribbon.MaxAutoRetries=1
# 切換實例的重試次數(shù) 默認(rèn)1
ribbon.MaxAutoRetriesNextServer=0

如果MaxAutoRetries=1MaxAutoRetriesNextServer=1請求在1s內(nèi)響應(yīng),超過1秒先同一個服務(wù)器上重試1次,如果還是超時或失敗,向其他服務(wù)上請求重試1次。

那么整個ribbon請求過程的超時時間為:ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)

以上就是關(guān)于“Ribbon如何使用”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI