第四章 客戶端負載均衡:Spring Cloud Ribbon

  spring cloud ribbon 是一個基於 HTTP 和 TCP 的客戶端負載均衡工具,它基於Netflix Ribbon 實現。經過Spring Cloud 的封裝,能夠輕鬆的將面向服務的REST模塊請求自動轉換爲客戶端負載均衡的服務調用。Spring Cloud Ribbon 雖然只是一個工具類框架,不像服務註冊中心、配置中心、API網關那樣須要獨立部署,但它幾乎存在於每個Spring Cloud 構建的微服務和基礎設施中。由於微服務間的調用,API網關的請求轉發等內容,實際上都是經過 Ribbon 來實現的,包括後續要介紹的 Feign,它也是基於Ribbon實現的工具。java

客戶端負載均衡

  負載均衡在系統架構中是一個很是重要,而且不得不去實施的內容。由於負載均衡是對系統的高可用、網絡壓力的緩解和處理能力擴容的重要手段之一。一般說的負載均衡都指的是服務端負載均衡,其中分爲硬件負載均衡和軟件負載均衡。硬件負載均衡主要是經過在服務器節點之間安裝專門用於負載均衡的設備,好比F5等;而軟件負載均衡則是經過在服務器上安裝一些具備負載均衡功能或模塊的軟件來完成請求分發工做,好比Nginx等。不論是硬件仍是軟件負載均衡,只要是服務器端負載均衡都能以相似下圖的架構方式構建起來:web

  硬件負載均衡的設備或是軟件負載均衡的軟件模塊都會維護一個下掛可用的服務端清單,經過心跳監測來剔除故障的服務端節點以保證清單中都是能夠正常訪問的服務端節點。當客戶端發送請求到負載均衡設備的時候,該設備按某種算法(好比線性輪詢、按權重負載、按流量負載等)從維護的可用服務端清單中取出一臺服務端的地址,而後進行轉發。算法

  而客戶端的負載均衡和服務端的負載均衡最大的不一樣點在於上面所提到的服務清單所存儲的位置。在客戶端負載均衡中,全部客戶端節點都維護着本身要訪問的服務端清單,而這些服務端的清單來自於服務註冊中心,好比上一章中介紹的Eureka服務端。同服務端負載均衡的架構相似,在客戶端負載均衡中也須要心跳去維護服務端清單的健康性,只是這個步驟須要與服務註冊中心配合完成。在Spring Cloud 實現的服務治理框架中,默認會建立針對各個服務治理框架的 Ribbon 自動化整合配置,好比 Eureka 中的 org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,Consul 中的 org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration。在實際使用的時候,能夠經過查看這兩個類的實現,以找到它們的配置詳情來幫助咱們更好的使用它。spring

  經過 Spring Cloud Ribbon 的封裝,咱們在微服務架構中使用客戶端負載均衡調用很是簡單,只須要以下兩步:數組

  • 服務提供者只須要啓動多個服務實例並註冊到一個註冊中心或是多個相關聯的服務註冊中心。
  • 服務消費者直接經過調用被 @LoadBalanced 註解修飾過的 RestTemplate 來實現面向服務的接口調用。

RestTemplate 詳解

  上一章中,咱們經過引入Ribbon實現了服務消費者的客戶端負載均衡功能。其中,咱們使用了一個很是有用的對象 RestTemplate 。該對象會使用 Ribbon 的自動化配置,同時經過配置 @LoadBalanced 還可以開啓客戶端負載均衡。以前演示了經過 RestTemplate 實現了最簡單的服務訪問,下面詳細介紹 RestTemplate 針對幾種不一樣請求類型和參數類型的服務調用實現。服務器

  GET請求

  在 RestTemplate 中,對GET請求能夠經過以下兩個方法進行調用。網絡

  第一種:getForEntity 函數。該方法返回的是 ResponseEntity,該對象是Spring 對 HTTP 請求響應的封裝,其中主要存儲了 HTTP 的幾個重要元素,好比 HTTP 請求狀態碼的枚舉對象 HttpStatus(404,500這些錯誤碼)、在它的父類 HttpEntity 中還存儲着 HTTP 請求的頭信息對象 HttpHeaders 以及泛型類型的請求體對象。好比下面的例子,就是訪問HELLO-SERVER服務的/index請求,同時最後一個參數didi 會替換 url 中的 {1} 佔位符,而返回的 ResponseEntity 對象中的 body 內容類型會根據第二個參數轉換爲 String 類型。架構

RestTemplate restTemplate = new RestTemplate ();
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/index?name={1}",String.class,"didi");
String body = responseEntity.getBody();

  若但願返回一個自定義類型,好比返回User類型,能夠把第二個參數換成User.class,以下:併發

RestTemplate restTemplate = new RestTemplate ();
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/index?name={1}",User.class,"didi");
User body = responseEntity.getBody();

  上面的例子是比較經常使用的方法,getForEntity 函數實際上提供了一下三種不一樣的重載實現。app

  • getForEntity(String url, Class<T> responseType, Object... uriVariables):url 爲請求地址,responseType 爲請求響應體body的包裝類型, uriVariables 爲 url 中的參數綁定。GET 請求的參數綁定一般使用url 中拼接的方式,好比 http://HELLO-SERVICE/index?name= didi ,但更好的方法是在 url 中使用佔位符並配合 uriVariables 參數實現GET 請求的參數綁定。因爲uriVariables 參數一個數組,因此它的順序會對應 url 中佔位符定義的數字順序。
  • getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables):這個方法和上述的方法只有uriVariables 的參數類型不一樣,表示此處須要在佔位符中指定Map中參數的key值,好比: http://HELLO-SERVICE/index?name={name},而Map類型的 uriVariables 中,就須要一個 key 爲name 的參數來綁定 url 中 {name} 佔位符的值。
  • getForEntity(URI url, Class<T> responseType):該方法使用 URI 對象來替代以前的 url 和 uriVariables 參數來指定訪問地址和參數綁定。URI 是 JDK java.net 包下的一個類,它表示一個統一資源標識符引用。

  第二種:getForObject 函數。該方法能夠理解爲對 getForEntity 的進一步封裝,它經過 HttpMessageConverterExtractor 對 HTTP 的請求響應體 body 內容進行對象轉換,實現請求直接返回包裝好的對象內容。好比:

RestTemplate restTemplate = new RestTemplate ();
String body = restTemplate .getForObject(url, String.class);

  當body 是一個User對象時,能夠直接這樣實現:

RestTemplate restTemplate = new RestTemplate ();
User body = restTemplate .getForObject(url, User.class);

  該方法也提供了三種不一樣的重載實現(參數和上面的方法相似):

  • getForObject(String url, Class<T> responseType, Object... uriVariables)
  • getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
  • getForObject(URI url, Class<T> responseType)

  POST請求

  在RestTemplate中,對POST請求時能夠經過以下三個方法進行調用實現。

  第一種:postForEntity 函數。使用方法和 getForEntity 函數相似

  postForEntity 函數也實現了三種不一樣的重載方法。

  • postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
  • postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
  • postForEntity(URI url, Object request, Class<T> responseType)

  這幾個函數的參數用法大部分都與 getForEntity 函數一致。

  第二種:postForObject 函數。該函數也和 getForObject 函數相似,它的做用是簡化 postForEntity 函數的後續處理。

  postForObject 函數也實現了三種不一樣的重載方法(使用方法和上述相似):

  • postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
  • postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
  • postForObject(URI url, Object request, Class<T> responseType)

  第三種:postForLocation 函數

  postForLocation 也實現了三種不一樣的重載方法(使用方法和上述相似):

  • postForLocation(String url, Object request, Object... uriVariables)
  • postForLocation(String url, Object request, Map<String, ?> uriVariables)
  • postForLocation(URI url, Object request)

  PUT請求、DELETE請求

  這兩個請求的函數都直接以 put 、delete 做爲方法名,使用方法和上述相似。

源碼分析

  從上述消費者示例中能夠發現, @LoadBalanced 註解實現了客戶端的負載均衡。經過搜索LoadBalancerClient 能夠發現,這是 Spring Cloud 中定義的一個接口:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.client.loadbalancer;

import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;

public interface LoadBalancerClient extends ServiceInstanceChooser {
    <T> T execute(String var1, LoadBalancerRequest<T> var2) throws IOException;

    <T> T execute(String var1, ServiceInstance var2, LoadBalancerRequest<T> var3) throws IOException;

    URI reconstructURI(ServiceInstance var1, URI var2);
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.client.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;

public interface ServiceInstanceChooser {
    ServiceInstance choose(String var1);
}

  從該接口中,咱們能夠經過定義的抽象方法來了解客戶端負載均衡器中應具有的幾種能力:

  • ServiceInstance choose(String serviceId):根據傳入的服務名serviceId ,從負載均衡器中挑選一個對應服務的實例。
  • T execute(String serviceId,LoadBalancerRequest request) throws IOException:使用從負載均衡器中挑選出的服務實例來執行請求內容。
  • URI reconstructURI(ServiceInstance instance,URI original):爲系統構建一個合適的host:port 形式的URI。在分佈式系統中,咱們使用邏輯上的服務名稱做爲host 來構建URI(替代服務實例的host:port形式)進行請求,好比 http://myservice/path/to/service。在該操做的定義中,前者 ServiceInstance 對象是帶有 host 和port 的具體服務實例,然後者URI 對象則是使用邏輯服務名定義爲 host 的URI,而返回的 URI 內容則是經過 ServiceInstance 的服務實例詳情拼接出的具體的 host:port 形式的請求地址。

  順着 LoadBalancerClient 接口的所屬包 org.springframework.cloud.client.loadbalancer,咱們對其內容進行整理,能夠得出以下圖所示的關係:

  從類名可初步判斷 loadBalancerAutoConfiguration 爲實現客戶端負載均衡器的自動化配置類。經過查看源碼,能夠獲得驗證:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.client.loadbalancer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestTransformer;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRetryProperties;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory.NeverRetryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
    @Autowired(
        required = false
    )
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(
        required = false
    )
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    public LoadBalancerAutoConfiguration() {
    }

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializer(final List<RestTemplateCustomizer> customizers) {
        return new SmartInitializingSingleton() {
            public void afterSingletonsInstantiated() {
                Iterator var1 = LoadBalancerAutoConfiguration.this.restTemplates.iterator();

                while(var1.hasNext()) {
                    RestTemplate restTemplate = (RestTemplate)var1.next();
                    Iterator var3 = customizers.iterator();

                    while(var3.hasNext()) {
                        RestTemplateCustomizer customizer = (RestTemplateCustomizer)var3.next();
                        customizer.customize(restTemplate);
                    }
                }

            }
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

    @Configuration
    @ConditionalOnClass({RetryTemplate.class})
    public static class RetryInterceptorAutoConfiguration {
        public RetryInterceptorAutoConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean
        public RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory) {
            return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, lbRetryPolicyFactory, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
            return new RestTemplateCustomizer() {
                public void customize(RestTemplate restTemplate) {
                    ArrayList list = new ArrayList(restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                }
            };
        }
    }

    @Configuration
    @ConditionalOnClass({RetryTemplate.class})
    public static class RetryAutoConfiguration {
        public RetryAutoConfiguration() {
        }

        @Bean
        public RetryTemplate retryTemplate() {
            RetryTemplate template = new RetryTemplate();
            template.setThrowLastExceptionOnExhausted(true);
            return template;
        }

        @Bean
        @ConditionalOnMissingBean
        public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
            return new NeverRetryFactory();
        }
    }

    @Configuration
    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return new RestTemplateCustomizer() {
                public void customize(RestTemplate restTemplate) {
                    ArrayList list = new ArrayList(restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                }
            };
        }
    }
}

  從該類的類註解可知,Ribbon 實現的負載均衡自動化配置須要知足下面兩個條件。

  • @ConditionOnClass(RestTemplate.class):RestTemplate 類必須存在於當前工程的環境中。
  • @ConditionOnBean(LoadBalancerClient.class):在Spring的Bean工程中必須有 LoadBalancerClient 的實現Bean。

  在該類中,主要作了下面三件事:

  • 建立了一個 LoadBalancerInterceptor 的 Bean,用於實現對客戶端發起請求時進行攔截,以實現客戶端負載均衡。
  • 建立了一個 RestTemplateCustomizer 的 Bean,用於給 RestTemplate 增長 LoadBalancerInterceptor 攔截器。
  • 維護了一個被 @LoadBalanced 註解修飾的 RestTemplate 對象列表,並在這裏進行初始化,經過調用 RestTemplateCustomizer 的實例來給須要客戶端負載均衡的 RestTemplate 增長 LoadBalancerInterceptor 攔截器。

  接着看下 LoadBalancerInterceptor 攔截器是如何講一個普通的 RestTemplate 變成客戶端負載均衡的:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.client.loadbalancer;

import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

  經過源碼以及以前的自動化配置類,咱們能夠看到在攔截器中注入了 LoadBalancerClient 的實現。當一個被 @LoadBalanced 註解修飾的 RestTemplate 對象向外發起 HTTP 請求時,會被 LoadBalancerInterceptor 類的 intercept 函數所攔截。因爲咱們在使用 RestTemplate 時採用了服務名做爲host,因此直接從 HttpRequest 的 URI 對象中經過 getHost 函數就能夠拿到服務名,而後調用 execute 函數去根據服務名來選擇實例併發起實際的請求。

  到此,LoadBalancerClient 還只是一個抽象的負載均衡器接口,因此咱們還須要找到它的具體實現類來進一步分析。經過查看 Ribbon 的源碼,能夠在 org.springframework.cloud.netflix.ribbon 包下找到對應的實現類 RibbonLoadBalancerClient 。

public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        Server server = this.getServer(loadBalancer);
        if(server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, ribbonServer, request);
        }
    }

    public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
        Server server = null;
        if(serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
            server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
        }

        if(server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
            RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

            try {
                Object ex = request.apply(serviceInstance);
                statsRecorder.recordStats(ex);
                return ex;
            } catch (IOException var8) {
                statsRecorder.recordStats(var8);
                throw var8;
            } catch (Exception var9) {
                statsRecorder.recordStats(var9);
                ReflectionUtils.rethrowRuntimeException(var9);
                return null;
            }
        }
    }

  能夠看到,在execute 函數的實現中,第一步作的就是經過 getServer 根據傳入的服務名 serviceId 去得到具體的服務實例:

 protected Server getServer(ILoadBalancer loadBalancer) {
        return loadBalancer == null?null:loadBalancer.chooseServer("default");
 }

  經過該函數的實現,能夠看到這裏獲取具體服務實例的時候並無使用 LoadBalancerClient 接口中的 choose 函數,而是使用了 Netflix Ribbon 自身的 ILoadBalancer 接口中定義的 chooseServer 函數。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.netflix.loadbalancer;

import com.netflix.loadbalancer.Server;
import java.util.List;

public interface ILoadBalancer {
    void addServers(List<Server> var1);

    Server chooseServer(Object var1);

    void markServerDown(Server var1);

    /** @deprecated */
    @Deprecated
    List<Server> getServerList(boolean var1);

    List<Server> getReachableServers();

    List<Server> getAllServers();
}

  能夠看到,在該接口中定義了一個客戶端負載均衡器須要的一系列抽象操做。

  • addServers:向負載均衡器中維護的實例列表增長服務實例。
  • chooseServer:經過某種策略,從負載均衡器中挑選一個具體的服務實例。
  • markServerDown:用來通知和標識負載均衡器中某個具體實例已經中止服務,否則負載均衡器在下一次獲取服務實例清單前會認爲服務實例均是正常服務的。
  • getReachableServers:獲取當前正常服務的實例列表。
  • getAllServers:獲取全部已知的服務實例列表,包括正常服務和中止服務的實例。

  在該接口定義中涉及的Server 對象定義是一個傳統的服務端節點,在該類中存儲了服務端節點的一些元數據信息,包括 host、port 以及一些部署信息等。

  對於該接口的實現,能夠整理出下圖所示的結構。能夠看到 BaseLoadBalancer 類實現了基礎的 負載均衡,而 DynamicServerListLoadBalancer 和 ZoneAwareLoadBalancer 在負載均衡的策略上作了一些功能的擴展。

  那麼在整合Ribbon的時候 Spring Cloud 默認採用了哪一個具體實現呢,能夠經過 RibbonClientConfiguration 配置類看出在整合時默認採用了 ZoneAwareLoadBalancer 來實現負載均衡器。

 @Bean
    @ConditionalOnMissingBean
    public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
        return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name)?(ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name):new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
    }

  下面回到 RibbonLoadBalancerClient 的 execute 函數邏輯,在經過 ZoneAwareLoadBalancer 的 chooseServer 函數獲取負載均衡策略分配到的服務實例對象 Server 後,將其內容包裝成 RibbonServer 對象,而後使用該對象再回調 LoadBalancerInterceptor 請求攔截器中 LoadBalancerRequest 的 apply(final ServiceInstance instance)函數,向一個實際的具體服務實例發起請求,從而實現一開始以服務名爲host 的 URI 請求到host:port形式的實際訪問地址的轉換。

  …………

負載均衡器

相關文章
相關標籤/搜索