定制设计Spring Cloud:负载均衡 - Spring Cloud Loadbalancer原理

Spring Cloud 2020版本以后,定制设计默认移除了对Netflix的依赖,定制设计其中就包括,定制设计官方默认推荐使用Spring Cloud Loadbalancer正式替换Ribbon,并成为了Spring Cloud定制设计负载均衡器的唯一实现。

定制设计今天我们深入分析一下Spring Cloud Loadbalancer定制设计的具体实现:

使用

1、公共依赖Spring Cloud,例如版本2020.0.2

  1. <dependency>
  2.         <groupId>org.springframework.cloud</groupId>
  3.         <artifactId>spring-cloud-dependencies</artifactId>
  4.         <version>2020.0.2</version>
  5.         <type>pom</type>
  6.         <scope>import</scope>
  7. </dependency>

注意:

如果是Hoxton定制设计之前的版本,默认器为Ribbon,需要移除Ribbon定制设计引用和增加配置spring.cloud.loadbalancer.ribbon.enabled: false

2、引入loadbalancer依赖

  1. <dependency>
  2.     <groupId>org.springframework.cloud</groupId>
  3.     <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  4. </dependency>
  5. <!-- 定制设计负载均衡需要搭配注册中心使用,这里引入Nacos定制设计做服务注册,也可采用Eureka等 -->
  6. <!-- SpringCloud Ailibaba Nacos -->
  7. <dependency>
  8.     <groupId>com.alibaba.cloud</groupId>
  9.     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  10. </dependency>

注意:

Nacos使用请参考官网:https://nacos.io/zh-cn/index.html

3、使用RestTemplate实现Demo

引入web依赖:

  1. <dependency>
  2.     <!-- 使用web,使用Spring MVC对外提供服务   -->
  3.     <groupId>org.springframework.boot</groupId>
  4.     <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>

编写DemoController:

  1. @RestController
  2. public class TestController {
  3.     @Autowired
  4.     private RestTemplate restTemplate;
  5.     // 新增restTemplate对象注入方法,注意,此处LoadBalanced注解一定要加上,否则无法远程调用
  6.     @Bean
  7.     @LoadBalanced
  8.     public RestTemplate restTemplate() {
  9.         return new RestTemplate();
  10.     }
  11.     @GetMapping("/load")
  12.     public String load() {
  13.         return restTemplate.getForObject("http://demo-server/hello/", String.class);
  14.     }
  15.     @GetMapping(value = "/hello")
  16.     public String hello() {
  17.         return "Hello World";
  18.     }
  19. }

4、启动Nacos,调用接口http://localhost:8080/load

原理

上面是RestTemplate负载均衡的简单实现,除此之外,Spring Cloud LoadBalancer还支持Spring Web Flux响应式编程,这里我们不展开,两者的实现原理思想相同,都是通过客户端添加拦截器,在拦截器中实现负载均衡。

1、#RestTemplate,提供了一个方法setInterceptors,用于设置拦截器,拦截器需要实现ClientHttpRequestInterceptor接口即可,在实际远程去请求服务端接口之前会先调用拦截器的intercept方法逻辑。这里的拦截器相当于Servlet技术中的Filter功能。

  1. // 代码实现在抽象父类InterceptingHttpAccessor里
  2. // RestTemplate.InterceptingHttpAccessor#setInterceptors
  3. public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
  4.    // Take getInterceptors() List as-is when passed in here
  5.    if (this.interceptors != interceptors) {
  6.       this.interceptors.clear();
  7.       this.interceptors.addAll(interceptors);
  8.       AnnotationAwareOrderComparator.sort(this.interceptors);
  9.    }
  10. }

2、#LoadBalancerAutoConfiguration,由于@LoadBalanced注解由spring-cloud-commons实现,查看实现逻辑我们发现spring-cloud-commons存在自动配置类LoadBalancerAutoConfiguration,当满足条件时,将自动创建LoadBalancerInterceptor并注入到RestTemplate中。

  1.     @Configuration(
  2.         proxyBeanMethods = false
  3.     )
  4.     @Conditional({LoadBalancerAutoConfiguration.RetryMissingOrDisabledCondition.class})
  5.     static class LoadBalancerInterceptorConfig {
  6.         LoadBalancerInterceptorConfig() {
  7.         }
  8.         @Bean
  9.         public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
  10.             return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
  11.         }
  12.         @Bean
  13.         @ConditionalOnMissingBean
  14.         public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
  15.             return (restTemplate) -> {
  16.                 List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
  17.                 list.add(loadBalancerInterceptor);
  18.                 restTemplate.setInterceptors(list);
  19.             };
  20.         }
  21.     }

3、#LoadRalancerLnterceptor,LoadBalancerInterceptor实现了ClientHttpRequestInterceptor接口,实现intercept方法,用于实现负载均衡的拦截处理。

  1. public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  2.     private LoadBalancerClient loadBalancer;
  3.     private LoadBalancerRequestFactory requestFactory;
  4.     public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
  5.         this.loadBalancer = loadBalancer;
  6.         this.requestFactory = requestFactory;
  7.     }
  8.     public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
  9.         this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
  10.     }
  11.     public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
  12.         URI originalUri = request.getURI();
  13.         String serviceName = originalUri.getHost();
  14.         Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
  15.         return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
  16.     }
  17. }

4、#LoadBalancerClient,负载均衡客户端,用于进行负载均衡逻辑,从服务列表中选择出一个服务地址进行调用。Spring Cloud LoadBalancer的默认实现为BlockingLoadBalancerClient,

  1.     public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  2.         String hint = this.getHint(serviceId);
  3.         LoadBalancerRequestAdapter<T, DefaultRequestContext> lbRequest = new LoadBalancerRequestAdapter(request, new DefaultRequestContext(request, hint));
  4.         Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);
  5.         supportedLifecycleProcessors.forEach((lifecycle) -> {
  6.             lifecycle.onStart(lbRequest);
  7.         });
  8.       //选择服务
  9.         ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);
  10.         if (serviceInstance == null) {
  11.             supportedLifecycleProcessors.forEach((lifecycle) -> {
  12.                 lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, new EmptyResponse()));
  13.             });
  14.             throw new IllegalStateException("No instances available for " + serviceId);
  15.         } else {
  16.             return this.execute(serviceId, serviceInstance, lbRequest);
  17.         }
  18.     }
  19.     public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
  20.         DefaultResponse defaultResponse = new DefaultResponse(serviceInstance);
  21.         Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);
  22.         Request lbRequest = request instanceof Request ? (Request)request : new DefaultRequest();
  23.         supportedLifecycleProcessors.forEach((lifecycle) -> {
  24.             lifecycle.onStartRequest(lbRequest, new DefaultResponse(serviceInstance));
  25.         });
  26.         try {
  27.             T response = request.apply(serviceInstance);
  28.             Object clientResponse = this.getClientResponse(response);
  29.             supportedLifecycleProcessors.forEach((lifecycle) -> {
  30.                 lifecycle.onComplete(new CompletionContext(Status.SUCCESS, lbRequest, defaultResponse, clientResponse));
  31.             });
  32.             return response;
  33.         } catch (IOException var9) {
  34.             supportedLifecycleProcessors.forEach((lifecycle) -> {
  35.                 lifecycle.onComplete(new CompletionContext(Status.FAILED, var9, lbRequest, defaultResponse));
  36.             });
  37.             throw var9;
  38.         } catch (Exception var10) {
  39.             supportedLifecycleProcessors.forEach((lifecycle) -> {
  40.                 lifecycle.onComplete(new CompletionContext(Status.FAILED, var10, lbRequest, defaultResponse));
  41.             });
  42.             ReflectionUtils.rethrowRuntimeException(var10);
  43.             return null;
  44.         }
  45.     }
  46. .... 
  47.     public ServiceInstance choose(String serviceId) {
  48.         return this.choose(serviceId, ReactiveLoadBalancer.REQUEST);
  49.     }
  50. //通过不同的负载均衡客户端实现选择不同的服务
  51.     public <T> ServiceInstance choose(String serviceId, Request<T> request) {
  52.         ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId);
  53.         if (loadBalancer == null) {
  54.             return null;
  55.         } else {
  56.             Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block();
  57.             return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer();
  58.         }
  59.     }

5、#LoadBalancerClientFactory,BlockingLoadBalancerClient中持有LoadBalancerClientFactory通过调用其getInstance方法获取具体的负载均衡客户端。客户端实现了不同的负载均衡算法,比如轮询、随机等。LoadBalancerClientFactory继承了NamedContextFactory,NamedContextFactory继承ApplicationContextAware,实现Spring ApplicationContext容器操作。

  1. public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification> implements Factory<ServiceInstance> {
  2.     public static final String NAMESPACE = "loadbalancer";
  3.     public static final String PROPERTY_NAME = "loadbalancer.client.name";
  4.     public LoadBalancerClientFactory() {
  5.         super(LoadBalancerClientConfiguration.class, "loadbalancer", "loadbalancer.client.name");
  6.     }
  7.     public String getName(Environment environment) {
  8.         return environment.getProperty("loadbalancer.client.name");
  9.     }
  10.     public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
  11.         return (ReactiveLoadBalancer)this.getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
  12.     }
  13. }

在spring-cloud-loadbalabcer中的LoadBalancerAutoConfiguration实现了LoadBalancerClientFactory缺省值:

  1.     @ConditionalOnMissingBean
  2.     @Bean
  3.     public LoadBalancerClientFactory loadBalancerClientFactory() {
  4.         LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
  5.         clientFactory.setConfigurations((List)this.configurations.getIfAvailable(Collections::emptyList));
  6.         return clientFactory;
  7.     }

6、#ReactiveLoadBalancer,负载均衡器,实现服务选择。Spring Cloud Balancer中实现了轮询RoundRobinLoadBalancer和随机数RandomLoadBalancer两种负载均衡算法。

如果没有显式指定负载均衡算法,默认缺省值为RoundRobinLoadBalancer。

LoadBalancerClientConfiguration#LoadBalancerClientConfiguration

  1.     @Bean
  2.     @ConditionalOnMissingBean
  3.     public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
  4.         String name = environment.getProperty("loadbalancer.client.name");
  5.         return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
  6.     }

7、#LoadBalancerRequestFactory,LoadBalancerRequest工厂类,用于创建LoadBalancerRequest,调用createRequest方法。在内部持有LoadBalancerClient属性对象,即BlockingLoadBalancerClient。

  1. public class LoadBalancerRequestFactory {
  2.     private LoadBalancerClient
  3. ;
  4.     private List<LoadBalancerRequestTransformer> transformers;
  5.     public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer, List<LoadBalancerRequestTransformer> transformers) {
  6.         this.loadBalancer = loadBalancer;
  7.         this.transformers = transformers;
  8.     }
  9.     public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer) {
  10.         this.loadBalancer = loadBalancer;
  11.     }
  12.     public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) {
  13.         return (instance) -> {
  14.             HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);
  15.             LoadBalancerRequestTransformer transformer;
  16.             if (this.transformers != null) {
  17.                 for(Iterator var6 = this.transformers.iterator(); var6.hasNext(); serviceRequest = transformer.transformRequest((HttpRequest)serviceRequest, instance)) {
  18.                     transformer = (LoadBalancerRequestTransformer)var6.next();
  19.                 }
  20.             }
  21.             return execution.execute((HttpRequest)serviceRequest, body);
  22.         };
  23.     }
  24. }

 

整合Feign

在日常项目中,一般负载均衡都是结合Feign使用,下面我们讨论下结合Fegin的使用情况

1、引入依赖

  1.         <!-- SpringCloud Openfeign -->
  2.         <dependency>
  3.             <groupId>org.springframework.cloud</groupId>
  4.             <artifactId>spring-cloud-starter-openfeign</artifactId>
  5.         </dependency>
  6.        <!-- 其他 loadbalancer依赖 -->

2、定义Feign接口

  1. @FeignClient(name = "my-service")
  2. public interface RemoteLogService {
  3.     @PostMapping("/sys/log")
  4.     R<Boolean> saveLog(@RequestBody SysLog sysLog);
  5. }

3、调用Feign接口调试(正常负载均衡已经可以使用,无需做其他配置)

4、原理分析

下图是Feign的实现原理,详情可参考博文:

4.1、查看Feign的loadbalancer的自动配置:FeignLoadBalancerAutoConfiguration,存在

LoadBalancerClient和LoadBalancerClientFactory的bean时,配置生效,默认使用DefaultFeignLoadBalancerConfiguration。请注意,如果引用了OkHttp或HttpClient,将使用不同的configuration文件。

  1. @ConditionalOnClass({Feign.class})
  2. @ConditionalOnBean({LoadBalancerClient.class, LoadBalancerClientFactory.class})
  3. @AutoConfigureBefore({FeignAutoConfiguration.class})
  4. @AutoConfigureAfter({BlockingLoadBalancerClientAutoConfiguration.class, LoadBalancerAutoConfiguration.class})
  5. @EnableConfigurationProperties({FeignHttpClientProperties.class})
  6. @Configuration(
  7. proxyBeanMethods = false
  8. )
  9. @Import({HttpClientFeignLoadBalancerConfiguration.class, OkHttpFeignLoadBalancerConfiguration.class, HttpClient5FeignLoadBalancerConfiguration.class, DefaultFeignLoadBalancerConfiguration.class})
  10. public class FeignLoadBalancerAutoConfiguration {
  11. public FeignLoadBalancerAutoConfiguration() {
  12. }
  13. }

4.2、#DefaultFeignLoadBalancerConfiguration,将缺省创建FeignBlockingLoadBalancerClient并注入LoadBalancerClient和LoadBalancerClientFactory,这两个bean的创建请参考上文。

  1. @Configuration(
  2. proxyBeanMethods = false
  3. )
  4. @EnableConfigurationProperties({LoadBalancerProperties.class})
  5. class DefaultFeignLoadBalancerConfiguration {
  6. DefaultFeignLoadBalancerConfiguration() {
  7. }
  8. @Bean
  9. @ConditionalOnMissingBean
  10. @Conditional({OnRetryNotEnabledCondition.class})
  11. public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
  12. return new FeignBlockingLoadBalancerClient(new Default((SSLSocketFactory)null, (HostnameVerifier)null), loadBalancerClient, properties, loadBalancerClientFactory);
  13. }
  14. ...
  15. }

4.3、#FeignBlockingLoadBalancerClient,实现excute方法,实现Feign具体请求操作,通过loadBalancerClient.choose获取实例并执行请求,具体选择逻辑和RestTemplate一致。

  1. public class FeignBlockingLoadBalancerClient implements Client {
  2. private final Client delegate;
  3. private final LoadBalancerClient loadBalancerClient;
  4. private final LoadBalancerProperties properties;
  5. private final LoadBalancerClientFactory loadBalancerClientFactory;
  6. public Response execute(Request request, Options options) throws IOException {
  7. URI originalUri = URI.create(request.url());
  8. String serviceId = originalUri.getHost();
  9. Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
  10. String hint = this.getHint(serviceId);
  11. DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest(new RequestDataContext(LoadBalancerUtils.buildRequestData(request), hint));
  12. Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(this.loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class), RequestDataContext.class, ResponseData.class, ServiceInstance.class);
  13. supportedLifecycleProcessors.forEach((lifecycle) -> {
  14. lifecycle.onStart(lbRequest);
  15. });
  16. ServiceInstance instance = this.loadBalancerClient.choose(serviceId, lbRequest);
  17. org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(instance);
  18. String message;
  19. if (instance == null) {
  20. message = "Load balancer does not contain an instance for the service " + serviceId;
  21. if (LOG.isWarnEnabled()) {
  22. LOG.warn(message);
  23. }
  24. supportedLifecycleProcessors.forEach((lifecycle) -> {
  25. lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, lbResponse));
  26. });
  27. return Response.builder().request(request).status(HttpStatus.SERVICE_UNAVAILABLE.value()).body(message, StandardCharsets.UTF_8).build();
  28. } else {
  29. message = this.loadBalancerClient.reconstructURI(instance, originalUri).toString();
  30. Request newRequest = this.buildRequest(request, message);
  31. return LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(this.delegate, options, newRequest, lbRequest, lbResponse, supportedLifecycleProcessors);
  32. }
  33. }
  34. }

 

 
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发