在上一博客ribbon源码分析之自定义配置、全局配置中我们已经初步了解了rebbon是如何自动配置如何进行自定义配置
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
那么就接探究LoadBalancerAutoConfiguration部分吧
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
首先自动注入被@LoadBalanced标记的restTemplates,不难看出@LoadBalanced是一个标记注解:
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
自动注入实现了LoadBalancerRequestTransformer的列表,用于对reques增加一些定制化需求
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
接下来是LoadBalancerInterceptorConfig
@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 restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
- 注册一个LoadBalancerInterceptor
- 把自动注入的restTemplate中添加拦截器LoadBalancerInterceptor
@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);
};
}
}
- 注册一个RetryLoadBalancerInterceptor
- 把自动注入的restTemplate中添加拦截RetryLoadBalancerInterceptor
LoadBalancerInterceptor有两个成员变量LoadBalancerClient和LoadBalancerRequestFactory
LoadBalancerClient负责重组url和执行请求
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* Executes 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;
/**
* Executes 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;
/**
* Creates a proper URI with a real host and port for systems to utilize.
* Some systems use a URI with the logical service 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);
}
并且继承ServiceInstanceChooser类,这个类提供了choose方法:选择具体一个服务实例
public interface ServiceInstanceChooser {
/**
* Chooses 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);
}
LoadBalancerRequestFactory的作用是为 LoadBalancerInterceptor 和 RetryLoadBalancerInterceptor 创建 LoadBalancerRequests。将 LoadBalancerRequestTransformers 应用于拦截的 HttpRequest。(实现LoadBalancerRequestTransformer接口客制化一些需求)
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
final byte[] body, final ClientHttpRequestExecution execution) {
return instance -> {
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
if (transformers != null) {
for (LoadBalancerRequestTransformer transformer : transformers) {
serviceRequest = transformer.transformRequest(serviceRequest, instance);
}
}
return execution.execute(serviceRequest, body);
};
}
LoadBalancerInterceptor只有一个方法intercept
@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);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
获得originalUri和serviceName使用requestFactory.createRequest获得真实的调用地址,调用LoadBalancerClient的execute方法执行请求。
而RetryLoadBalancerInterceptor的intercept方法如下
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);
//获取重试策略
final LoadBalancedRetryPolicy retryPolicy = lbRetryFactory.createRetryPolicy(serviceName,
loadBalancer);
RetryTemplate template = createRetryTemplate(serviceName, request, retryPolicy);
return template.execute(context -> {
ServiceInstance serviceInstance = null;
if (context instanceof LoadBalancedRetryContext) {
LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
//获取服务实例
serviceInstance = lbContext.getServiceInstance();
}
if (serviceInstance == null) {
serviceInstance = loadBalancer.choose(serviceName);
}
//执行请求
ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer.execute(
serviceName, serviceInstance,
requestFactory.createRequest(request, body, execution));
int statusCode = response.getRawStatusCode();
//如果重试策略不为null并且返回code在配置的code中,抛出异常,可以做到根据返回码来控制是否要重试
if (retryPolicy != null && retryPolicy.retryableStatusCode(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;
}
});
}
比之LoadBalancerInterceptor的成员变量,不同的是RibbonLoadBalancedRetryFactory,
RibbonLoadBalancedRetryFactory主要负责创建重试策略、重试监听等
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();
}
}
RibbonLoadBalancedRetryPolicy主要用来判断是否能够满足重试条件,主要代码如下
/**
* Return true to retry the failed request on the same server.
* This method may be called more than once when executing a single operation.
* @param context The context for the retry operation.
* @return True to retry the failed request on the same server; false otherwise.
*/
public boolean canRetrySameServer(LoadBalancedRetryContext context);
/**
* Return true to retry the failed request on the next server from the load balancer.
* This method may be called more than once when executing a single operation.
* @param context The context for the retry operation.
* @return True to retry the failed request on the next server from the load balancer; false otherwise.
*/
public boolean canRetryNextServer(LoadBalancedRetryContext context);
/**
* Called when the retry operation has ended.
* @param context The context for the retry operation.
*/
public abstract void close(LoadBalancedRetryContext context);
/**
* Called when the execution fails.
* @param context The context for the retry operation.
* @param throwable The throwable from the failed execution.
*/
public abstract void registerThrowable(LoadBalancedRetryContext context, Throwable throwable);
/**
* If an exception is not thrown when making a request, this method will be called to see if the
* client would like to retry the request based on the status code returned. For example, in
* Cloud Foundry, the router will return a <code>404</code> when an app is not available. Since
* HTTP clients do not throw an exception when a <code>404</code> is returned,
* <code>retryableStatusCode</code> allows clients to force a retry.
* @param statusCode The HTTP status code.
* @return True if a retry should be attempted; false to just return the response.
*/
public boolean retryableStatusCode(int statusCode);
@Override
public boolean canRetrySameServer(LoadBalancedRetryContext context) {
return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
}
@Override
public boolean canRetryNextServer(LoadBalancedRetryContext context) {
//this will be called after a failure occurs and we increment the counter
//so we check that the count is less than or equals to too make sure
//we try the next server the right number of times
return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
}
其中canRetry接口用来判断请求类型是否允许重试,默认只允许GET请求能够重试,如果需要所有的请求都能实现重试,那么需要设置OkToRetryOnAllOperations为true(当然不建议这么做)。
public boolean canRetry(LoadBalancedRetryContext context) {
HttpMethod method = context.getRequest().getMethod();
return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
}
canRetryNextServer主要用来判断是否满足调用下一个服务的条件。lbContext.getRetryHandler()是默认的DefaultLoadBalancerRetryHandler,
protected RetryHandler defaultRetryHandler = new DefaultLoadBalancerRetryHandler();
public DefaultLoadBalancerRetryHandler() {
this.retrySameServer = 0;
this.retryNextServer = 0;
this.retryEnabled = false;
}
retrySameServer:默认情况下是0,即重试自身服务的次数为0,
retryNextServer:默认情况下是0,即重试下个服务的次数为0,
retryEnabled:即是否说有请求都重试,默认是false。
到此可以总结
- 自动注入@LoadBalanced标记的restTemplates
- 自动注入实现了LoadBalancerRequestTransformer的transformers
- 创建LoadBalancerRequestFactory,为 LoadBalancerInterceptor 和 RetryLoadBalancerInterceptor 创建 LoadBalancerRequests。将 LoadBalancerRequestTransformers 应用于拦截的 HttpRequest。
- 为restTemplate添加LoadBalancerInterceptor和RetryInterceptorAutoConfiguration
负载均衡的内容关于LoadBalancerClient的具体实现也尤为重要(LoadBalancerRequestFactory中通过LoadBalancerClient来执行请求),将在下一篇博客中单独介绍