Springcloud-loadbalancer-04

381 阅读9分钟

png4.png 👉现在我们将目光放到org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration

/**
 * Auto-configuration for blocking client-side load balancing.
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Will Tran
 * @author Gang Li
 * @author Olga Maciaszek-Sharma
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
public class LoadBalancerAutoConfiguration {

   @LoadBalanced
   @Autowired(required = false)
   //收集所有的restTemplates
   private List<RestTemplate> restTemplates = Collections.emptyList();

   @Autowired(required = false)
   private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

   @Bean
   //收集所有的restTemplateCustomizers对restTemplates进行定制
   public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
         final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
      return () -> restTemplateCustomizers.ifAvailable(customizers -> {
         for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
            for (RestTemplateCustomizer customizer : customizers) {
               //使用RestTemplateCustomizer.customize定制restTemplate
               customizer.customize(restTemplate);
            }
         }
      });
   }

   @Bean
   @ConditionalOnMissingBean
   public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
      return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
   }

   @Configuration(proxyBeanMethods = false)
   @Conditional(RetryMissingOrDisabledCondition.class)
   static class LoadBalancerInterceptorConfig {

      @Bean
      //创建一个LoadBalancerInterceptor拦截器,用来负载均衡用
      public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }

      @Bean
      @ConditionalOnMissingBean
      //将loadBalancerInterceptor拦截器添加到restTemplate中,当我们使用restTemplate执行
      //远程调用的时候就会被该拦截器拦截
      public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
         return restTemplate -> {
            List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
            list.add(loadBalancerInterceptor);
            restTemplate.setInterceptors(list);
         };
      }

   }

   private static class RetryMissingOrDisabledCondition extends AnyNestedCondition {

      RetryMissingOrDisabledCondition() {
         super(ConfigurationPhase.REGISTER_BEAN);
      }

      @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
      static class RetryTemplateMissing {

      }

      @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
      static class RetryDisabled {

      }

   }

   /**
    * Auto configuration for retry mechanism.
    */
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass(RetryTemplate.class)
   public static class RetryAutoConfiguration {

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

   }

   /**
    * Auto configuration for retry intercepting mechanism.
    */
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass(RetryTemplate.class)
   @ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
   @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", matchIfMissing = true)
   public static class RetryInterceptorAutoConfiguration {

      @Bean
      @ConditionalOnMissingBean
      public RetryLoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory,
            ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
         return new RetryLoadBalancerInterceptor(loadBalancerClient, requestFactory, loadBalancedRetryFactory,
               loadBalancerFactory);
      }

      @Bean
      @ConditionalOnMissingBean
      //将RetryLoadBalancerInterceptor添加到restTemplate中,以实现重试
      public RestTemplateCustomizer restTemplateCustomizer(
            final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
         return restTemplate -> {
            List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
            list.add(loadBalancerInterceptor);
            restTemplate.setInterceptors(list);
         };
      }

   }

}

上面配置的逻辑不难理解,收集容器中的RestTemplate,并为其添加拦截器。

先创建了LoadBalancerRequestFactory,它主要用来创建LoadBalancerRequest,调用createRequest方法。接着从LoadBalancerInterceptorConfig中来看,创建了LoadBalancerInterceptor,并传入了loadBalancerClientLoadBalancerRequestFactory,loadBalancerClient后面在详细说明,接着在restTemplateCustomizer中将loadBalancerInterceptor添加到restTemplate中。其实这里有个扩展点,如果我们想要统计restTemplate每次调用的时间,可以添加一个拦截器,不过这里添加的是负载均衡拦截器。我们看下该拦截器具体做了啥,以便后续我们扩展。

//负载均衡拦截器,通过实现ClientHttpRequestInterceptor接口的intercept方法实现拦截功能
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

   //负载均衡客户端,用于在服务列表中选择出一个服务地址进行调用,默认实现是BlockingLoadBalancerClient
   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));
   }

   @Override
   //LoadBalancerInterceptor具体的拦截方法
   public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
         final ClientHttpRequestExecution execution) throws IOException {
      //获取请求的URI
      final URI originalUri = request.getURI();
      //获取请求的服务名
      String serviceName = originalUri.getHost();
      Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
      //使用loadBalanceClient执行具体的请求
      return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
   }

}

主要看intercept方法,它在最后会调用LoadBalancerClient执行请求,默认的是BlockingLoadBalancerClient后续我们在说。

此时我们应该猜到,当我们使用RestTemplate进行远程调用的时候,请求会被封装,并被LoadBalancerInterceptor拦截器拦截,在LoadBalancerInterceptorintercept方法中执行远程调用。

现在我们再看RetryInterceptorAutoConfiguration,该类创建了RetryLoadBalancerInterceptor并将其添加到restTemplate中。

public class RetryLoadBalancerInterceptor implements ClientHttpRequestInterceptor {

	private static final Log LOG = LogFactory.getLog(RetryLoadBalancerInterceptor.class);

	private final LoadBalancerClient loadBalancer;

	private final LoadBalancerRequestFactory requestFactory;

	private final LoadBalancedRetryFactory lbRetryFactory;

	private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory;

	/**
	 * @deprecated in favour of
	 * {@link RetryLoadBalancerInterceptor#RetryLoadBalancerInterceptor(LoadBalancerClient, LoadBalancerRequestFactory, LoadBalancedRetryFactory, ReactiveLoadBalancer.Factory)}
	 */
	@Deprecated
	public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerProperties properties,
			LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory lbRetryFactory,
			ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
		this.lbRetryFactory = lbRetryFactory;
		this.loadBalancerFactory = loadBalancerFactory;
	}

	public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory,
			LoadBalancedRetryFactory lbRetryFactory,
			ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
		this.lbRetryFactory = lbRetryFactory;
		this.loadBalancerFactory = loadBalancerFactory;
	}

	@Override
	//重试拦截器
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		final String serviceName = originalUri.getHost();
		Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
		//创建重试策略,利用微服务名称还有LoadBalancerClient,创建一个LoadBalancedRetryPolicy
		final LoadBalancedRetryPolicy retryPolicy = lbRetryFactory.createRetryPolicy(serviceName, loadBalancer);
		//创建重试模版
		RetryTemplate template = createRetryTemplate(serviceName, request, retryPolicy);
		//设置执行回调并执行请求
		return template.execute(context -> {
			ServiceInstance serviceInstance = null;
			//之后我们会看到,在调用异常,发生重试并切换server时,
			// 就是从LoadBalancer中重新choose一个Server并放入这个lbContext,在这里把他取出来
			if (context instanceof LoadBalancedRetryContext) {
				LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
				//在LoadBalancedRetryContext中获取需要重试的实例
				serviceInstance = lbContext.getServiceInstance();
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format(" service instance from LoadBalancedRetryContext: %s",
							serviceInstance));
				}
			}
			Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
					.getSupportedLifecycleProcessors(
							loadBalancerFactory.getInstances(serviceName, LoadBalancerLifecycle.class),
							RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
			String hint = getHint(serviceName);
			//如果是null代表是第一次被回调
			if (serviceInstance == null) {
				if (LOG.isDebugEnabled()) {
					LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
							+ "Reattempting service instance selection");
				}
				ServiceInstance previousServiceInstance = null;
				if (context instanceof LoadBalancedRetryContext) {
					LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
					//获取先前重试的实例
					previousServiceInstance = lbContext.getPreviousServiceInstance();
				}
				DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
						new RetryableRequestContext(previousServiceInstance, new RequestData(request), hint));
				supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
				//获取服务实例
				serviceInstance = loadBalancer.choose(serviceName, lbRequest);
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Selected service instance: %s", serviceInstance));
				}
				if (context instanceof LoadBalancedRetryContext) {
					LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
					//设置该服务实例到重试上下文中
					lbContext.setServiceInstance(serviceInstance);
				}
				Response<ServiceInstance> lbResponse = new DefaultResponse(serviceInstance);
				if (serviceInstance == null) {
					supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
							.onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
									CompletionContext.Status.DISCARD,
									new DefaultRequest<>(
											new RetryableRequestContext(null, new RequestData(request), hint)),
									lbResponse)));
				}
			}
			LoadBalancerRequestAdapter<ClientHttpResponse, RetryableRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(
					requestFactory.createRequest(request, body, execution),
					new RetryableRequestContext(null, new RequestData(request), hint));
			ServiceInstance finalServiceInstance = serviceInstance;
			//在这个serviceInstance上面执行Request
			ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(serviceName,
					finalServiceInstance, lbRequest);
			//获取返回statusCode
			int statusCode = response.getRawStatusCode();
			//如果发出请求时未引发异常,则将调用此方法以查看客户端是否希望根据返回的状态代码重试请求。
			// 例如,在Cloud Foundry中,当应用程序不可用时,路由器将返回<code>404</code>。
			// 由于HTTP客户端在返回<code>404</code>时不会引发异常,
			// <code>retryableStatusCode</code>允许客户端强制重试。
			//可以设置retryableStatusCodes
			if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Retrying on status code: %d", statusCode));
				}
				byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());
				response.close();
				throw new ClientHttpResponseStatusCodeException(serviceName, response, bodyCopy);
			}
			return response;
		}, new LoadBalancedRecoveryCallback<ClientHttpResponse, ClientHttpResponse>() {
			// This is a special case, where both parameters to
			// LoadBalancedRecoveryCallback are
			// the same. In most cases they would be different.
			@Override
			protected ClientHttpResponse createResponse(ClientHttpResponse response, URI uri) {
				return response;
			}
		});
	}

	private RetryTemplate createRetryTemplate(String serviceName, HttpRequest request,
			LoadBalancedRetryPolicy retryPolicy) {
		RetryTemplate template = new RetryTemplate();
		//创建回退策略
		BackOffPolicy backOffPolicy = lbRetryFactory.createBackOffPolicy(serviceName);
		template.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
		//设置如果重试次数用尽,是否将最后一个Exception抛出
		template.setThrowLastExceptionOnExhausted(true);
		//创建重试监听器
		RetryListener[] retryListeners = lbRetryFactory.createRetryListeners(serviceName);
		if (retryListeners != null && retryListeners.length != 0) {
			//将监听器设置到回退模版中
			template.setListeners(retryListeners);
		}
		//设置重试策略
		template.setRetryPolicy(
				//此处读取LoadBalancerProperties中的配置,查看是否需要重试,
				!loadBalancerFactory.getProperties(serviceName).getRetry().isEnabled() || retryPolicy == null
						? new NeverRetryPolicy()//不需要重试
						: new InterceptorRetryPolicy(request, retryPolicy, loadBalancer, serviceName)); //需要重试
		return template;
	}

	private String getHint(String serviceId) {
		Map<String, String> hint = loadBalancerFactory.getProperties(serviceId).getHint();
		String defaultHint = hint.getOrDefault("default", "default");
		String hintPropertyValue = hint.get(serviceId);
		return hintPropertyValue != null ? hintPropertyValue : defaultHint;
	}

}

我们先看一下成员变量,其中LoadBalancerClientLoadBalancedRetryFactory可能没介绍过。这里先不说LoadBalancerClient,我们只需知道它定义了客户端负载均衡的执行规范,默认的配置类是BlockingLoadBalancerClient,后续会说到。LoadBalancedRetryFactory需要注意一下。

/**
 * Factory class used to customize the retry functionality throughout Spring Cloud.
 * 可以实现该接口,定义重试策略
 * @author Ryan Baxter
 */
public interface LoadBalancedRetryFactory {

   /**
    * Creates a {@link LoadBalancedRetryPolicy}.
    * @param service The ID of the service to create the retry policy for.
    * @param serviceInstanceChooser Used to get the next server from a load balancer.
    * @return A retry policy for the service.
    */
   default LoadBalancedRetryPolicy createRetryPolicy(String service, ServiceInstanceChooser serviceInstanceChooser) {
      return null;
   }

   /**
    * Creates an array of {@link RetryListener}s for a given service.
    * @param service The service to create the {@link RetryListener}s for.
    * @return An array of {@link RetryListener}s.
    */
   //创建一组监听器
   default RetryListener[] createRetryListeners(String service) {
      return new RetryListener[0];
   }

   /**
    * Creates a {@link BackOffPolicy} for a given service.
    * @param service The service to create the {@link BackOffPolicy} for.
    * @return The {@link BackOffPolicy}.
    */
   //创建回退策略
   default BackOffPolicy createBackOffPolicy(String service) {
      return new NoBackOffPolicy();
   }

}

该接口定义了重试策略的规范,我们在看下它的默认实现类BlockingLoadBalancedRetryFactory

/**
 * An implementation of {@link LoadBalancedRetryFactory} for
 * {@link BlockingLoadBalancerClient}.
 *
 * @author Olga Maciaszek-Sharma
 * @since 2.2.6
 */
//重试
public class BlockingLoadBalancedRetryFactory implements LoadBalancedRetryFactory {

   private final ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory;

   public BlockingLoadBalancedRetryFactory(ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
      this.loadBalancerFactory = loadBalancerFactory;
   }

   @Override
   public LoadBalancedRetryPolicy createRetryPolicy(String serviceId, ServiceInstanceChooser serviceInstanceChooser) {
      return new BlockingLoadBalancedRetryPolicy(loadBalancerFactory.getProperties(serviceId));
   }

}

从介绍中可以看出它是为BlockingLoadBalancerClient服务的,BlockingLoadBalancerClient先不说。主要看他创建的BlockingLoadBalancedRetryPolicy

/**
 * A {@link LoadBalancedRetryPolicy} implementation for
 * {@link BlockingLoadBalancerClient}. Based on <code>RibbonLoadBalancedRetryPolicy</code>
 * to achieve feature-parity.
 * 负载均衡的重试策略-基于RibbonLoadBalancedRetryPolicy实现
 * @author Olga Maciaszek-Sharma
 * @since 2.2.6
 */
public class BlockingLoadBalancedRetryPolicy implements LoadBalancedRetryPolicy {

   private final LoadBalancerProperties properties;

   private int sameServerCount = 0;

   private int nextServerCount = 0;

   public BlockingLoadBalancedRetryPolicy(LoadBalancerProperties properties) {
      this.properties = properties;
   }

   //检查是否需要重试
   public boolean canRetry(LoadBalancedRetryContext context) {
      HttpMethod method = context.getRequest().getMethod();
      ///如果是Get方法,就能Retry
      // 或者配置了isOkToRetryOnAllOperations为true(默认是false),就无论什么httpmethod都重试
      return HttpMethod.GET.equals(method) || properties.getRetry().isRetryOnAllOperations();
   }

   @Override
   //检查是否要在同一个Server上执行重试
   public boolean canRetrySameServer(LoadBalancedRetryContext context) {
      ////当sameServerCount大于等于MaxRetriesOnSameServer
      //(就是MaxAutoRetries这个配置)并且可以retry的时候
      return sameServerCount < properties.getRetry().getMaxRetriesOnSameServiceInstance() && canRetry(context);
   }

   @Override
   public boolean canRetryNextServer(LoadBalancedRetryContext context) {
      // After the failure, we increment first and then check, hence the equality check
      //失败后,我们首先递增,然后检查,因此进行相等性检查
      return nextServerCount <= properties.getRetry().getMaxRetriesOnNextServiceInstance() && canRetry(context);
   }

   @Override
   public void close(LoadBalancedRetryContext context) {

   }

   @Override
   public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
      //检查
      if (!canRetrySameServer(context) && canRetry(context)) {
         // Reset same server since we are moving to a new ServiceInstance
         //由于切换到下一个server,所以就置零sameServerCount
         sameServerCount = 0;
         nextServerCount++;
         //如果不能canRetryNextServer,就表明耗尽重试次数
         if (!canRetryNextServer(context)) {
            context.setExhaustedOnly();
         }
         else {
            // We want the service instance to be set by
            // `RetryLoadBalancerInterceptor`
            // in order to get the entire data of the request
            context.setServiceInstance(null);
         }
      }
      else {
         sameServerCount++;
      }
   }

   @Override
   public boolean retryableStatusCode(int statusCode) {
      //触发重试的状态码,可以设置
      return properties.getRetry().getRetryableStatusCodes().contains(statusCode);
   }

}

看到了吧,这就是默认的负载均衡策略,介绍中可以看出,它是基于ribbon实现,虽然springcloud摒弃了netfix体系,但是源码中还有其影子。具体描述已经注释,后续用到了详细说。我们回到RetryLoadBalancerInterceptor中来,分析下intercept方法。

我们知道这些拦截器会在RestTemplate调用的时候执行,并执行它的intercept方法,

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
      final ClientHttpRequestExecution execution) throws IOException {
  ....
    //创建重试策略,利用微服务名称还有LoadBalancerClient,创建一个LoadBalancedRetryPolicy
		final LoadBalancedRetryPolicy retryPolicy = lbRetryFactory.createRetryPolicy(serviceName, loadBalancer);
   //创建重试模版
		RetryTemplate template = createRetryTemplate(serviceName, request, retryPolicy);
  ....
}

先创建重试策略,也就是上面提到的BlockingLoadBalancedRetryPolicy。其次创建RetryTemplate

private RetryTemplate createRetryTemplate(String serviceName, HttpRequest request,
      LoadBalancedRetryPolicy retryPolicy) {
   RetryTemplate template = new RetryTemplate();
   //创建回退策略
   BackOffPolicy backOffPolicy = lbRetryFactory.createBackOffPolicy(serviceName);
   template.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
   //设置如果重试次数用尽,是否将最后一个Exception抛出
   template.setThrowLastExceptionOnExhausted(true);
   //创建重试监听器
   RetryListener[] retryListeners = lbRetryFactory.createRetryListeners(serviceName);
   if (retryListeners != null && retryListeners.length != 0) {
      //将监听器设置到回退模版中
      template.setListeners(retryListeners);
   }
   //设置重试策略
   template.setRetryPolicy(
         //此处读取LoadBalancerProperties中的配置,查看是否需要重试,
         !loadBalancerFactory.getProperties(serviceName).getRetry().isEnabled() || retryPolicy == null
               ? new NeverRetryPolicy()//不需要重试
               : new InterceptorRetryPolicy(request, retryPolicy, loadBalancer, serviceName)); //需要重试
   return template;
}

需要说明的是创建回退策略可以自定义实现,默认的BlockingLoadBalancedRetryPolicy并没有重写,因此默认是没有回退策略的。

此外还有监听器。最后设置重试策略,如果重试属性是开启的话,就创建InterceptorRetryPolicy,这其实是包装器,它包装了retryPolicy,默认的就是BlockingLoadBalancedRetryPolicy,因此InterceptorRetryPolicy中的多数操作,其实就是操作BlockingLoadBalancedRetryPolicy

@Override
//重试拦截器
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
      final ClientHttpRequestExecution execution) throws IOException {
  ...
  return template.execute(context -> {
			ServiceInstance serviceInstance = null;
			//之后我们会看到,在调用异常,发生重试并切换server时,
			// 就是从LoadBalancer中重新choose一个Server并放入这个lbContext,在这里把他取出来
			if (context instanceof LoadBalancedRetryContext) {
				LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
				//在LoadBalancedRetryContext中获取需要重试的实例
				serviceInstance = lbContext.getServiceInstance();
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format(" service instance from LoadBalancedRetryContext: %s",
							serviceInstance));
				}
			}
			Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
					.getSupportedLifecycleProcessors(
							loadBalancerFactory.getInstances(serviceName, LoadBalancerLifecycle.class),
							RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
			String hint = getHint(serviceName);
			//如果是null代表是第一次被回调
			if (serviceInstance == null) {
				if (LOG.isDebugEnabled()) {
					LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
							+ "Reattempting service instance selection");
				}
				ServiceInstance previousServiceInstance = null;
				if (context instanceof LoadBalancedRetryContext) {
					LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
					//获取先前重试的实例
					previousServiceInstance = lbContext.getPreviousServiceInstance();
				}
				DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
						new RetryableRequestContext(previousServiceInstance, new RequestData(request), hint));
				supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
				//获取服务实例
				serviceInstance = loadBalancer.choose(serviceName, lbRequest);
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Selected service instance: %s", serviceInstance));
				}
				if (context instanceof LoadBalancedRetryContext) {
					LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
					//设置该服务实例到重试上下文中
					lbContext.setServiceInstance(serviceInstance);
				}
				Response<ServiceInstance> lbResponse = new DefaultResponse(serviceInstance);
				if (serviceInstance == null) {
					supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
							.onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
									CompletionContext.Status.DISCARD,
									new DefaultRequest<>(
											new RetryableRequestContext(null, new RequestData(request), hint)),
									lbResponse)));
				}
			}
			LoadBalancerRequestAdapter<ClientHttpResponse, RetryableRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(
					requestFactory.createRequest(request, body, execution),
					new RetryableRequestContext(null, new RequestData(request), hint));
			ServiceInstance finalServiceInstance = serviceInstance;
			//在这个serviceInstance上面执行Request
			ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(serviceName,
					finalServiceInstance, lbRequest);
			//获取返回statusCode
			int statusCode = response.getRawStatusCode();
			//如果发出请求时未引发异常,则将调用此方法以查看客户端是否希望根据返回的状态代码重试请求。
			// 例如,在Cloud Foundry中,当应用程序不可用时,路由器将返回<code>404</code>。
			// 由于HTTP客户端在返回<code>404</code>时不会引发异常,
			// <code>retryableStatusCode</code>允许客户端强制重试。
			//可以设置retryableStatusCodes
			if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Retrying on status code: %d", statusCode));
				}
				byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());
				response.close();
				throw new ClientHttpResponseStatusCodeException(serviceName, response, bodyCopy);
			}
			return response;
		}, new LoadBalancedRecoveryCallback<ClientHttpResponse, ClientHttpResponse>() {
			// This is a special case, where both parameters to
			// LoadBalancedRecoveryCallback are
			// the same. In most cases they would be different.
			@Override
			protected ClientHttpResponse createResponse(ClientHttpResponse response, URI uri) {
				return response;
			}
		});
}

template.execute其实调用的是RetryTemplate.doExecute方法,

protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState state) throws E, ExhaustedRetryException {
		...
    try {
        ...
        } else {
         		...
            while(true) {
                Object var34;
               //canRetry判断是否可以重试,最后调用的是LoadBalancedRetryPolicy.canRetryNextServer(lbContext)方法。
              //其实就是默认的BlockingLoadBalancedRetryPolicy.canRetryNextServer(lbContext)
                if (this.canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
                    try {
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Retry: count=" + context.getRetryCount());
                        }

                        lastException = null;
                        //回调上面的执行回调,如果调用成功就会返回这个响应
										    //上面回调会检查响应码,在我们上面项目配置中,如果响应码为我们设定的值,
                        //就会抛出RetryableStatusCodeException,其他响应吗因为没													
                        //有抛出异常,所以不会触发重试
                        var34 = retryCallback.doWithRetry(context);
                        return var34;
                    } catch (Throwable var31) {
                       //捕获异常,记录到lastException
                        Throwable e = var31;
                        lastException = var31;

                        try {
                            //主要就是调用Policy的registerThrowable
                            this.registerThrowable(retryPolicy, state, context, e);
                        } catch (Exception var28) {
                            throw new TerminatedRetryException("Could not register throwable", var28);
                        } finally {
                            this.doOnErrorInterceptors(retryCallback, context, var31);
                        }

                       ...
            }
        }
    } catch (Throwable var32) {
        ...
    } finally {
     ...
    }
}

​ 我们注释了关键的代码,我们可以看出它先canRetry检查是否可以重试,如果可以重试再进行方法回调,等下再具体说明回调。如果回调发送异常,捕获异常,记录到lastException,并调用Policy的registerThrowable

👉我们按照上述的顺序先看下canRetry方法,先调用的是InterceptorRetryPolicy.canRetry,如下所示:

@Override
public boolean canRetry(RetryContext context) {
   LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
   if (lbContext.getRetryCount() == 0 && lbContext.getServiceInstance() == null) {
      // We haven't even tried to make the request yet so return true so we do
     //意思就是此时还没有重试过呢
      lbContext.setServiceInstance(null);
      return true;
   }
   return policy.canRetryNextServer(lbContext);
}

上述方法意思是如果重试次数为0并且重试的实例为null,此时就表示还没有重试过,直接返回true,如果不是则调用 policy.canRetryNextServer(lbContext)。我们用默认的实现来说下该方法。BlockingLoadBalancedRetryPolicy.canRetryNextServer(lbContext)来了

@Override
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
   // After the failure, we increment first and then check, hence the equality check
   //失败后,我们首先递增,然后检查,相等性检查
   return nextServerCount <= properties.getRetry().getMaxRetriesOnNextServiceInstance() && canRetry(context);
}

该方法中nextServerCount表示重试的次数,他在每次异常重试时会递增,properties.getRetry().getMaxRetriesOnNextServiceInstance()获取的是我们配置的最大重试次数(到现在我们一直没说怎么配置,我想稍微熟悉Spring的都会的,简单说下spring.cloud.loadbalancer.retry.maxRetriesOnNextServiceInstance=3,这里说明一下当你配置时可能没有提示,那是因为官方在additional-spring-configuration-metadata.json中并没有配置,有兴趣的自己参考官方的配置,自己配一下就行了)。言归正传该方法就是重试的次数要小于我们配置的次数。再看下它的canRetry(context);方法

//检查是否需要重试
public boolean canRetry(LoadBalancedRetryContext context) {
   HttpMethod method = context.getRequest().getMethod();
   ///如果是Get方法,就能Retry
   // 或者配置了isOkToRetryOnAllOperations为true(默认是false),就无论什么httpmethod都重试
   return HttpMethod.GET.equals(method) || properties.getRetry().isRetryOnAllOperations();
}

注释解释的很明白了,不多说了。

👉现在我们将RetryTemplatecanRetry方法已经说完,我们仔细看下回调方法,此处重新复制粘贴一下。

@Override
//重试拦截器
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
      final ClientHttpRequestExecution execution) throws IOException {
  ...
  return template.execute(context -> {
			ServiceInstance serviceInstance = null;
			//之后我们会看到,在调用异常,发生重试并切换server时,
			// 就是从LoadBalancer中重新choose一个Server并放入这个lbContext,在这里把他取出来
			if (context instanceof LoadBalancedRetryContext) {
				LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
				//在LoadBalancedRetryContext中获取需要重试的实例
				serviceInstance = lbContext.getServiceInstance();
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format(" service instance from LoadBalancedRetryContext: %s",
							serviceInstance));
				}
			}
			Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
					.getSupportedLifecycleProcessors(
							loadBalancerFactory.getInstances(serviceName, LoadBalancerLifecycle.class),
							RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
			String hint = getHint(serviceName);
			//如果是null代表是第一次被回调
			if (serviceInstance == null) {
				if (LOG.isDebugEnabled()) {
					LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
							+ "Reattempting service instance selection");
				}
				ServiceInstance previousServiceInstance = null;
				if (context instanceof LoadBalancedRetryContext) {
					LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
					//获取先前重试的实例
					previousServiceInstance = lbContext.getPreviousServiceInstance();
				}
				DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
						new RetryableRequestContext(previousServiceInstance, new RequestData(request), hint));
				supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
				//获取服务实例
				serviceInstance = loadBalancer.choose(serviceName, lbRequest);
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Selected service instance: %s", serviceInstance));
				}
				if (context instanceof LoadBalancedRetryContext) {
					LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
					//设置该服务实例到重试上下文中
					lbContext.setServiceInstance(serviceInstance);
				}
				Response<ServiceInstance> lbResponse = new DefaultResponse(serviceInstance);
				if (serviceInstance == null) {
					supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
							.onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
									CompletionContext.Status.DISCARD,
									new DefaultRequest<>(
											new RetryableRequestContext(null, new RequestData(request), hint)),
									lbResponse)));
				}
			}
			LoadBalancerRequestAdapter<ClientHttpResponse, RetryableRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(
					requestFactory.createRequest(request, body, execution),
					new RetryableRequestContext(null, new RequestData(request), hint));
			ServiceInstance finalServiceInstance = serviceInstance;
			//在这个serviceInstance上面执行Request
			ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(serviceName,
					finalServiceInstance, lbRequest);
			//获取返回statusCode
			int statusCode = response.getRawStatusCode();
			//如果发出请求时未引发异常,则将调用此方法以查看客户端是否希望根据返回的状态代码重试请求。
			// 例如,在Cloud Foundry中,当应用程序不可用时,路由器将返回<code>404</code>。
			// 由于HTTP客户端在返回<code>404</code>时不会引发异常,
			// <code>retryableStatusCode</code>允许客户端强制重试。
			//可以设置retryableStatusCodes
			if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {
				if (LOG.isDebugEnabled()) {
					LOG.debug(String.format("Retrying on status code: %d", statusCode));
				}
				byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());
				response.close();
				throw new ClientHttpResponseStatusCodeException(serviceName, response, bodyCopy);
			}
			return response;
		}, new LoadBalancedRecoveryCallback<ClientHttpResponse, ClientHttpResponse>() {
			// This is a special case, where both parameters to
			// LoadBalancedRecoveryCallback are
			// the same. In most cases they would be different.
			@Override
			protected ClientHttpResponse createResponse(ClientHttpResponse response, URI uri) {
				return response;
			}
		});
}

其中serviceInstance = loadBalancer.choose(serviceName, lbRequest);我们后面再说,此处我们只需知道它可以获取服务实例。

具体的注释已经在上面展示了,接下来我们最后看下如果异常时会如何,此时回到RetryTemplate.doExecute方法会捕捉该异常,并执行this.registerThrowable(retryPolicy, state, context, e);,最终下去会调用BlockingLoadBalancedRetryPolicy.registerThrowable方法,我们看下:

@Override
//异常时执行
public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
   //检查
   if (!canRetrySameServer(context) && canRetry(context)) {
      // Reset same server since we are moving to a new ServiceInstance
      //由于切换到下一个server,所以就置零sameServerCount
      sameServerCount = 0;
      nextServerCount++;
      //如果不能canRetryNextServer,就表明耗尽重试次数
      if (!canRetryNextServer(context)) {
         context.setExhaustedOnly();
      }
      else {
         // We want the service instance to be set by
         // `RetryLoadBalancerInterceptor`
         // in order to get the entire data of the request
         context.setServiceInstance(null);
      }
   }
   else {
      sameServerCount++;
   }
}

上述逻辑就是先判断是否可以重试,如果可以的话重试次数加一,并将sameServerCount清零,其中canRetrySameServer(context)乳如下:

@Override
//检查是否要在同一个Server上执行重试
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
   ////当sameServerCount大于等于MaxRetriesOnSameServer
   //(就是MaxAutoRetries这个配置)并且可以retry的时候
   return sameServerCount < properties.getRetry().getMaxRetriesOnSameServiceInstance() && canRetry(context);
}

其中sameServerCount表示在同一台服服务实例中执行重试的次数,默认是0,意味这默认只执行一次。其他的看注释就可以明白了。

🤓其实已经将重试介绍完了,但读者可能会有点乱,我们用流程图说明下:

retry.png 介绍完了重试后我们看下第一张图我们完成多少。

png5.png