一、简介
Spring Cloud Ribbon 是一个基于 Http 和 TCP 的客服端负载均衡工具,它是基于 Netflix Ribbon 实现的。它不像服务注册中心、配置中心、API 网关那样独立部署,但是它几乎存在于每个微服务的基础设施中。包括前面的提供的声明式服务调用也是基于该 Ribbon 实现的。理解 Ribbon 对于我们使用 Spring Cloud 来讲非常的重要,因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。在上节的例子中,我们采用了声明式的方式来实现负载均衡。实际上,内部调用维护了一个 RestTemplate 对象,该对象会使用 Ribbon 的自动化配置,同时通过 @LoadBalanced 开启客户端负载均衡。其实 RestTemplate 是 Spring 自己提供的对象,不是新的内容。读者不知道 RestTemplate 可以查看相关的文档。
二、探索
我们可以从 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 这个接口定义:
- /**
- * 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 自动化配置类。注释如下:
- /**
- * 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,该类用于实现对客户端发起请求时进行拦截,以实现客户端的负载均衡。在需要更多的章节才能理清其关系,我还要细细的阅读,等阅读差不多在给大家讲讲。
三、配置
当于 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
来源: http://www.cnblogs.com/liferecord/p/6893786.html