1、簡介 java
Spring Cloud Ribbon 是一個基於Http和TCP的客服端負載均衡工具,它是基於Netflix Ribbon實現的。它不像服務註冊中心、配置中心、API網關那樣獨立部署,可是它幾乎存在於每一個微服務的基礎設施中。包括前面的提供的聲明式服務調用也是基於該Ribbon實現的。理解Ribbon對於咱們使用Spring Cloud來說很是的重要,由於負載均衡是對系統的高可用、網絡壓力的緩解和處理能力擴容的重要手段之一。在上節的例子中,咱們採用了聲明式的方式來實現負載均衡。實際上,內部調用維護了一個RestTemplate對象,該對象會使用Ribbon的自動化配置,同時經過@LoadBalanced開啓客戶端負載均衡。其實RestTemplate是Spring本身提供的對象,不是新的內容。讀者不知道RestTemplate能夠查看相關的文檔。spring
2、探索網絡
咱們能夠從LoadBalanced開始追蹤:負載均衡
/** * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient * @author Spencer Gibb */ @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface LoadBalanced { }
從其該註解的定義註釋咱們能夠知道,該註解用來給RestTemplate作標記,以使用負載均衡的客戶端(LoadBalancerClient)來配置RestTemplate。接着咱們來查看一下LoadBalancerClient這個接口定義:ide
/** * Represents a client side load balancer * @author Spencer Gibb */ public interface LoadBalancerClient extends ServiceInstanceChooser { /** * execute request using a ServiceInstance from the LoadBalancer for the specified * service * @param serviceId the service id to look up the LoadBalancer * @param request allows implementations to execute pre and post actions such as * incrementing metrics * @return the result of the LoadBalancerRequest callback on the selected * ServiceInstance */ <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; /** * execute request using a ServiceInstance from the LoadBalancer for the specified * service * @param serviceId the service id to look up the LoadBalancer * @param serviceInstance the service to execute the request to * @param request allows implementations to execute pre and post actions such as * incrementing metrics * @return the result of the LoadBalancerRequest callback on the selected * ServiceInstance */ <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; /** * Create a proper URI with a real host and port for systems to utilize. * Some systems use a URI with the logical serivce name as the host, * such as http://myservice/path/to/service. This will replace the * service name with the host:port from the ServiceInstance. * @param instance * @param original a URI with the host as a logical service name * @return a reconstructed URI */ URI reconstructURI(ServiceInstance instance, URI original); }
LoadBalancerClient 是集成 ServiceInstanceChooser,接着咱們查看一下該接口定義:
public interface ServiceInstanceChooser { /** * Choose a ServiceInstance from the LoadBalancer for the specified service * @param serviceId the service id to look up the LoadBalancer * @return a ServiceInstance that matches the serviceId */ ServiceInstance choose(String serviceId); }
從上面的註解中,咱們能夠知道 choose()方法根據傳入的serviceId服務Id,從負載均衡器選擇一個一個對應的服務實例。execute()方法根據serviceId服務ID和請求request來執行請求內容。reconstructURI()方法構建出一個合適的Host:Port的URI。而 RibbonLoadBalancerClient就是LoadBalancerClient的具體實現。微服務
接着咱們查看LoadBalancerClient所在的包,結構以下:工具
咱們發現有一個類咱們須要去關注一下:LoadBalancerAutoConfiguration ,從其源碼註解中咱們知道是一個爲Ribbon 自動化配置類。註釋以下:post
/** * Auto configuration for Ribbon (client side load balancing). * * @author Spencer Gibb * @author Dave Syer * @author Will Tran */ @Configuration @ConditionalOnClass(RestTemplate.class) //條件 : RestTemplate必須在工程的類路徑下 @ConditionalOnBean(LoadBalancerClient.class) //條件: Spring 容器中必須包含LoadBalancerClient的實現,即RibbonLoadBalancerClient @EnableConfigurationProperties(LoadBalancerRetryProperties.class) //啓動重試功能,能夠spring.cloud.loadbalancer.retry=false,取消重試,默認參數爲true public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); //維護一個RestTemplate列表,經過LoadBalanced來註解。 @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer( //加載初始話自定義的restTeplate,實質是初始化InterceptingHttpAccessor具體調用 final List<RestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } } }; } @Autowired(required = false) private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); @Bean @ConditionalOnMissingBean public LoadBalancerRequestFactory loadBalancerRequestFactory( LoadBalancerClient loadBalancerClient) { return new LoadBalancerRequestFactory(loadBalancerClient, transformers); } @Configuration @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class 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() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) static class RetryAutoConfiguration { @Bean public RetryTemplate retryTemplate() { RetryTemplate template = new RetryTemplate(); template.setThrowLastExceptionOnExhausted(true); return template; } @Bean @ConditionalOnMissingBean public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() { return new LoadBalancedRetryPolicyFactory.NeverRetryFactory(); } @Bean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate(), properties, lbRetryPolicyFactory, requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( //自定義RestTemplate ,實質是初始化InterceptingHttpAccessor final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } }
從上面代碼得知,咱們須要瞭解一下LoadBalancerInterceptor,該類用於實現對客戶端發起請求時進行攔截,以實現客戶端的負載均衡。在須要更多的章節才能理清其關係,我還要細細的閱讀,等閱讀差很少在給你們講講。ui
3、配置this
當於Spring Cloud應用引入Ribbon和Eureka的時候,會觸發Eureka中實現的Ribbon的自動化配置。
serverList 的維護機制是由 DiscoveryEnabledNIWSServerList的實例維護,該類會將服務清單列表交給Eureka的服務治理機制來維護。
IPing的實現由 NIWSDiscoveryPing 的實例維護,該類也將服務檢查交給Eureka的服務治理機制來維護。
默認狀況下,用於獲取實例請求的ServiceList接口實現是採用Spring Cloud Eureka中封裝的DomainExtractingServerList.因爲Spring Cloud Ribbon默認實現了區域親和策略,因此咱們能夠經過Eureka實例的元數據配置來實現區域化的實例配置方案。
好比 eureka.instance.metadataMap.zone=hangzhou. 經過zone參數來指定本身所在的區域。
在Spring Cloud Ribbon 與 Spring Cloud Eureka結合工程中,咱們能夠經過參數配置方式來禁用Eureka對Ribbon服務實例的維護實現。
ribbon.eureka.enabled=false