分享免费的编程资源和教程

网站首页 > 技术教程 正文

springCloud负载均衡机制 springcloud负载均衡策略有哪些

goqiw 2024-09-25 20:13:40 技术教程 25 ℃ 0 评论

前言

在微服务体系中,为了保证服务高可用和高性能,服务提供者会运行多个实例并注册到注册中心中,消费者在调用提供者接口时需要选择其中具体的某个实例发起请求,此为客户端负载均衡。

springCloud基于RestTemplate抽象出负载均衡过程,消费者通过http://APP-PROVIDER/user/add这种url格式去访问提供者接口,其中 APP-PROVIDER 这个是 提供者服务的应用名(准确来说是在注册中心的serviceId,一般serviceId是应用名),那么肯定需要在发起http请求调用之前把 APP-PROVIDER 转换成 ip:port形式。 按照spring的习惯,此处应该有拦截器实现,拦截器作用是

1、根据APP-PROVIDER找到所有的运行实例

2、从诸多实例中根据一定算法挑选其中一个

3、解析成http://ip:port形式

一、@LoadBalanced

负载均衡标志注解

作用:@LoadBalanced修饰的RestTemplate实例是一个具备负载均衡能力的restTemplate

没有@LoadBalanced修饰的RestTemplate则为普通的restTemplate

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

二、ServiceInstanceChooser

serviceInstance选择器,serviceId 通常是指application name,ServiceInstance是一个实例

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

三、LoadBalancerClient

负载均衡客户端或者负载均衡器

execute方法主要做两件事:

1、根据某种负载均衡算法通过serviceId挑选具体某个实例

2、调用LoadBalancerRequest.apply方法

public interface LoadBalancerClient extends ServiceInstanceChooser {
   <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
   <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
   // 将http://APP-PROVIDER 格式 转成 http://host:port
   URI reconstructURI(ServiceInstance instance, URI original);
}

四、LoadBalancerRequest

具备负载均衡功能的Request请求对象,封装好LoadBalancerRequest,再apply一下就能得到响应。apply的时机应该会在LoadBalancerClient挑选某个实例之后apply一下

public interface LoadBalancerRequest<T> {

   public T apply(ServiceInstance instance) throws Exception;

}

五、LoadBalancerRequestFactory

创建LoadBalancerRequest的工厂类

public class LoadBalancerRequestFactory {

   private LoadBalancerClient loadBalancer;
   private List<LoadBalancerRequestTransformer> transformers;

   public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
         final byte[] body, final ClientHttpRequestExecution execution) {
      //LoadBalancerRequest的apply方法实现是个lambda表达式
      return instance -> {
             // 利用装饰模式,将httpRequest包装成具有负载均衡能力的 LoadBalancerRequest
             //然后就通过 execution.execute(serviceRequest, body)调用, 典型的拦截器执行方法
            HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
            if (transformers != null) {
                for (LoadBalancerRequestTransformer transformer : transformers) {
                    serviceRequest = transformer.transformRequest(serviceRequest, instance);
                }
            }
            return execution.execute(serviceRequest, body);
        };
   }

}

六、ServiceRequestWrapper

装饰模式封装普通的httpRequest,最重要的是getURI方法,可以将http://APP-PROVIDER转换成 http://ip:port

public class ServiceRequestWrapper extends HttpRequestWrapper {
   private final ServiceInstance instance;
   private final LoadBalancerClient loadBalancer;

   public ServiceRequestWrapper(HttpRequest request, ServiceInstance instance,
                         LoadBalancerClient loadBalancer) {
      super(request);
      this.instance = instance;
      this.loadBalancer = loadBalancer;
   }

   @Override
   public URI getURI() {
      URI uri = this.loadBalancer.reconstructURI(
            this.instance, getRequest().getURI());
      return uri;
   }
}

七、LoadBalancerInterceptor

负载均衡拦截器,作用就是restTemplate发起真正的http请求之前执行负载均衡客户端的execute方法

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) {
      // for backwards compatibility
      this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
   }

  /**因为使用的是restTemplate,在有拦截器的场景中,参数request其实是个 InterceptingClientHttpRequest
  * execution是 InterceptingRequestExecution
  */
   @Override
   public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
         final ClientHttpRequestExecution execution) throws IOException {
      final URI originalUri = request.getURI();
      String serviceName = originalUri.getHost();
      Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
      // 每次请求都会通过LoadBalancerRequestFactory创建一个新的LoadBalancerRequest 
     return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
   }
}

八、LoadBalancerAutoConfiguration

负载均衡自动配置,应用启动时会自动加载到

这个类最重要的作用就是给 利用RestTemplateCustomizer 给 restTemplate 增加一个拦截器LoadBalancerInterceptor


@Configuration
@ConditionalOnClass(RestTemplate.class) //依赖 RestTemplate
@ConditionalOnBean(LoadBalancerClient.class) //依赖具体的LoadBalancerClient实例,例如RibbonLoadBalancerClient,说明系统中一定要有一个具体的负载均衡实现才会加载此类
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

   //将用 @LoadBalanced修饰的restTemplate对象加载到 restTemplates
   @LoadBalanced
   @Autowired(required = false)
   private List<RestTemplate> restTemplates = Collections.emptyList();

   //SmartInitializingSingleton,在IOC所有单例被实例化后会回调,见DefaultListableBeanFactory.preInstantiateSingletons
   //此处作用是等restTemplate对象和 loadBalancerInterceptor实例完之后,执行RestTemplateCustomizer.customize来给restTemplate增加拦截器
   @Bean
   public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
         final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
      return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            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 {
      // 创建负载均衡拦截,看命名 ribbonInterceptor,说明springCloud还是看重 Ribbon
      @Bean
      public LoadBalancerInterceptor ribbonInterceptor(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }
     // RestTemplateCustomizer用lambda表达式,
     // 作用就是 将 loadBalancerInterceptor 加入到restTemplate拦截器集合。
      @Bean
      @ConditionalOnMissingBean
      public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
         return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor); //此处特别注意是把负载均衡拦截器放到最后一个,意味着最后执行负载均衡拦截器
                restTemplate.setInterceptors(list);
            };
      }
   }

   /**
   * 这下面是关于Retry重试方面的配置,可以放单章讲解spring-retry的模型
   * retry就是指请求失败根据一定策略重新多请求几次
   */
   @Configuration
   @ConditionalOnClass(RetryTemplate.class)
   public static class RetryAutoConfiguration {

      @Bean
      @ConditionalOnMissingBean
      public LoadBalancedRetryFactory loadBalancedRetryFactory() {
         return new LoadBalancedRetryFactory() {};
      }
   }

   @Configuration
   @ConditionalOnClass(RetryTemplate.class)
   public static class RetryInterceptorAutoConfiguration {
      @Bean
      @ConditionalOnMissingBean
      public RetryLoadBalancerInterceptor ribbonInterceptor(
            LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
            LoadBalancerRequestFactory requestFactory,
            LoadBalancedRetryFactory loadBalancedRetryFactory) {
         return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
               requestFactory, loadBalancedRetryFactory);
      }

      @Bean
      @ConditionalOnMissingBean
      public RestTemplateCustomizer restTemplateCustomizer(
            final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
         return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
      }
   }
}

九、总结

负载均衡整体过程

1、应用启动阶段

根据自动装置机制LoadBalancerAutoConfiguration,给restTemplate设置一个负载均衡拦截器LoadBalancerInterceptor

2、restTemplate请求阶段

(1)InterceptingRequestExecution会执行拦截器LoadBalancerInterceptor,LoadBalancerInterceptor执行LoadBalancerClient的execute方法

(2)LoadBalancerClient根据负载均衡算法挑选实例,并执行LoadBalancerRequest.apply

(3)LoadBalancerRequest利用装饰模式转换成ServiceRequestWrapper

(4)拦截器执行链InterceptingRequestExecution在执行的时候会调用ServiceRequestWrapper.getURI,此时会利用LoadBalancerClient 将http://APP-PROVIDER转换成http://ip:port ,再发起真正的请求。


springCloud将整个负载均衡调用过程进行抽象,利用RestTemplate (spring-web5.0里的RestTemplate实现原理 ),仅仅只是增加一个拦截器就做到了负载均衡。能够看出最重要的一个地方就是 LoadBalancerClient

十、springCloud如何整合Ribbon?

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表