概述

在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力,先前有细嚼过但是没有做过笔记,刚好处理此类问题记录下

@LoadBalanced

  1. /**
  2. * 注释将RestTemplate bean标记为配置为使用LoadBalancerClient。
  3. */
  4. @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Documented
  7. @Inherited
  8. @Qualifier
  9. public @interface LoadBalanced {
  10. }

通过源码可以发现这是一个LoadBalanced标记注解并且标记了@Qualifier(基于Spring Boot的自动配置机制),我们可以溯源到LoadBalancerAutoConfiguration

LoadBalancerAutoConfiguration

  1. /**
  2. * 功能区的自动配置(客户端负载平衡)
  3. */
  4. @Configuration
  5. @ConditionalOnClass(RestTemplate.class)
  6. @ConditionalOnBean(LoadBalancerClient.class)
  7. @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
  8. public class LoadBalancerAutoConfiguration {
  9. @LoadBalanced
  10. @Autowired(required = false)
  11. private List<RestTemplate> restTemplates = Collections.emptyList(); //这里持有@LoadBalanced标记的RestTemplate实例
  12. @Autowired(required = false)
  13. private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
  14. @Bean
  15. public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
  16. final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
  17. return () -> restTemplateCustomizers.ifAvailable(customizers -> {
  18. for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
  19. for (RestTemplateCustomizer customizer : customizers) {
  20. //为restTemplate添加定制
  21. customizer.customize(restTemplate);
  22. }
  23. }
  24. });
  25. }
  26. // ...
  27. /**
  28. * 以下针对classpath存在RetryTemplate.class的情况配置,先忽略
  29. */
  30. @Configuration
  31. @ConditionalOnClass(RetryTemplate.class)
  32. public static class RetryAutoConfiguration {
  33. @Bean
  34. @ConditionalOnMissingBean
  35. public LoadBalancedRetryFactory loadBalancedRetryFactory() {
  36. return new LoadBalancedRetryFactory() {
  37. };
  38. }
  39. }
  40. // ...
  41. }

@LoadBalanced@Autowried结合使用,意思就是这里注入的RestTempate Bean是所有加有@LoadBalanced注解标记的(持有@LoadBalanced标记的RestTemplate实例)

这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。

关键问下自己:为什么?

  • RestTemplate实例是怎么被收集的?
  • 怎样通过负载均衡规则获取具体的具体的server?

继续扒看源码>
上面可以看出,会LoadBalancerAutoConfiguration类对我们加上@LoadBalanced注解的bean 添加loadBalancerInterceptor拦截器

LoadBalancerInterceptor

  1. /**
  2. * 功能区的自动配置(客户端负载平衡)。
  3. */
  4. public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  5. private LoadBalancerClient loadBalancer;
  6. private LoadBalancerRequestFactory requestFactory;
  7. public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
  8. LoadBalancerRequestFactory requestFactory) {
  9. this.loadBalancer = loadBalancer;
  10. this.requestFactory = requestFactory;
  11. }
  12. public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
  13. // for backwards compatibility
  14. this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
  15. }
  16. @Override
  17. public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
  18. final ClientHttpRequestExecution execution) throws IOException {
  19. final URI originalUri = request.getURI();
  20. String serviceName = originalUri.getHost();
  21. Assert.state(serviceName != null,
  22. "Request URI does not contain a valid hostname: " + originalUri);
  23. return this.loadBalancer.execute(serviceName,
  24. this.requestFactory.createRequest(request, body, execution));
  25. }
  26. }

重点看intercept方法 当我们restTemplate执行请求操作时,就会被拦截器拦截进入intercept方法,而loadBalancer是LoadBalancerClient的具体实现

RibbonLoadBalancerClient

  1. public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
  2. throws IOException {
  3. ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
  4. Server server = getServer(loadBalancer, hint);
  5. if (server == null) {
  6. throw new IllegalStateException("No instances available for " + serviceId);
  7. }
  8. RibbonServer ribbonServer = new RibbonServer(serviceId, server,
  9. isSecure(server, serviceId),
  10. serverIntrospector(serviceId).getMetadata(server));
  11. return execute(serviceId, ribbonServer, request);
  12. }

看到这里相信都遇到过类似的错误,恍然大悟

  1. No instances available for xxxxx

总结

  • 1.根据serviceId 获取对应的loadBalancer
  • 2.根据loadBalancer获取具体的server(这里根据负载均衡规则,获取到具体的服务实例)
  • 3.创建RibbonServer
  • 4.执行具体请求

这里

注意: @LoadBalanced 标记注解获取到最后通过负载均衡规则获取具体的具体的server来发起请求

案例

  1. /**
  2. * 服务注册中心配置
  3. *
  4. * @author <a href="mailto:shangzhi.ibyte@gmail.com">iByte</a>
  5. * @since 1.0.1
  6. */
  7. @Configuration
  8. @EnableConfigurationProperties(ModuleMappingHelper.class)
  9. public class DiscoveryConfig {
  10. @Autowired
  11. Environment environment;
  12. /**
  13. * DiscoveryHeaderHelper默认bean
  14. * @return
  15. */
  16. @Bean
  17. public DiscoveryHeaderHelper discoveryHeaderHelper() {
  18. DiscoveryHeaderHelper discoveryHeaderHelper = new DiscoveryHeaderHelper(environment);
  19. DiscoveryHeaderHelper.INSTANCE = discoveryHeaderHelper;
  20. return discoveryHeaderHelper;
  21. }
  22. /**
  23. * resttemplate构建
  24. */
  25. @Resource
  26. private RestTemplateBuilder restTemplateBuilder;
  27. /**
  28. * resttemplate请求bean,更改系统本身的builder
  29. * @return
  30. */
  31. @Bean
  32. @LoadBalanced
  33. public RestTemplate restTemplate() {
  34. RestTemplate restTemplate = restTemplateBuilder.configure(new RestTemplate());
  35. //RestTemplate interceptors 远程调用请求增加头部信息处理
  36. restTemplate.getInterceptors().add(new RestApiHeaderInterceptor());
  37. //RestTemplate Set the error handler 错误处理
  38. restTemplate.setErrorHandler(new RestResponseErrorHandler());
  39. return restTemplate;
  40. }
  41. @Bean
  42. public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
  43. DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs();
  44. discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new DiscoveryHeaderClientFilter()));
  45. discoveryClientOptionalArgs.setEventListeners(Collections.singleton(new EurekaClientEventListener()));
  46. return discoveryClientOptionalArgs;
  47. }
  48. }

源码地址 > DiscoveryConfig