This article talks about how springcloud integrates ribbon. Different springcloud components (feign, zuul, RestTemplate) integrate ribbon differently. This article first takes a look at RestTemplate.
The class diagram of RestTemplate is as follows
##HttpAccessor is mainly created based on
ClientHttpRequestFactory ##ClientHttpRequest
HttpAccessor to create an intercepted
InterceptingClientHttpRequest, where the interception will be set Device
ClientHttpRequestInterceptor, which is the core of the integrated ribbon. When
RestTemplate initiates an http request call, it will first go through the interceptor and then actually initiate the http request.
LoadBalancerAutoConfiguration class, there is the following code:
@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();
As long as the annotation
@LoadBalanced is added, the RestTemplate will be injected. If spring retry is not introduced, When loading the component, load the following configuration:
@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 new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } }
In this way,
RestTemplate is set to LoadBalancerInterceptor. Let’s take a look at the entire calling processThe whole process is a bit complicated. The core is to initiate a load balancing call through the interceptor LoadBalancerInterceptor and RibbonLoadBalancerClient. RibbonLoadBalancerClientI combines LoadBalancer, so it has the ability to load balance, which is the ribbon principle we explained in the previous article.
We did not draw the actual process of initiating an http request in the figure. The default is created bySimpleClientHttpRequestFactory
. The class diagram ofClientHttpRequestFactory is as follows:
##We can see from the calling sequence diagram that at first we called InterceptingClientHttpRequestFactory to obtain
InterceptingClientHttpRequest, which are combined The method integrates ClientHttpRequestFactory
and the interceptor. When InterceptingClientHttpRequest
initiates the call, it delegates its internal class InterceptingRequestExecution
to handle it. The core logic is: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}else {
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
List<String> values = entry.getValue();
for (String value : values) {
delegate.getHeaders().add(entry.getKey(), value);
}
}
if (body.length > 0) {
StreamUtils.copy(body, delegate.getBody());
}
return delegate.execute();
}
}</pre><div class="contentsignin">Copy after login</div></div>
First, the first execution of the interceptor collection will be taken out. When the execution of the interceptor is completed, it will be called back, execute the else code, and actually initiate the http request. There are two main ways to implement the ClientHttpRequestFactory
interface:
One is
One way is to use the
RestTemplate defaults to using SimpleClientHttpRequestFactory, which internally calls HttpConnection of jdk. The default timeout is -1. You can set the timeout like this:
@Bean @LoadBalanced public RestTemplate restTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(1000 * 2);//连接超时时间 factory.setReadTimeout(1000 * 1);//读超时时间 return new RestTemplate(factory); }
You can use a connection pool (recommended), and you can also set a retry strategy (not studied specifically)
If you want to enable the retry mechanism, we can introduce spring's retry component<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;"><dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>版本号</version>
</dependency></pre><div class="contentsignin">Copy after login</div></div>
In this way, springcloud-ribbon will add the following configuration:
@Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean public RetryTemplate retryTemplate() { RetryTemplate template = new RetryTemplate(); template.setThrowLastExceptionOnExhausted(true); return template; } @Bean @ConditionalOnMissingBean public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() { return new LoadBalancedRetryPolicyFactory.NeverRetryFactory(); } } @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryInterceptorAutoConfiguration { @Bean @ConditionalOnMissingBean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, lbRetryPolicyFactory, requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } }
@Bean @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate") @ConditionalOnMissingBean public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) { return new RibbonLoadBalancedRetryPolicyFactory(clientFactory); }
The interceptor is replaced with
RetryLoadBalancerInterceptor, where the retry component retryTemplate is integrated. The retry strategy is configured by the
RetryHandler interface. The default implementation class is DefaultLoadBalancerRetryHandler
. The following are the default configuration parameters <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">#最大的重试次数
ribbon.MaxAutoRetries=0
#最大重试server的个数
ribbon.MaxAutoRetriesNextServer=1
#是否开启任何异常都重试(默认在get请求下会重试,其他情况不会重试,除非设置为true)
ribbon.OkToRetryOnAllOperations=false
#指定重试的http状态码
ribbon.retryableStatusCodes=500,501</pre><div class="contentsignin">Copy after login</div></div>. The above is globally effective. If ## is added #xxx.ribbon.MaxAutoRetries=1<code>This will only take effect on a certain ribbon client. MaxAutoRetries and MaxAutoRetriesNextServer are used together. The maximum number of retries is for each server. If MaxAutoRetries=1 and MaxAutoRetriesNextServer=1 are set, the maximum number of retries triggered is 4 times.
The above is the detailed content of Detailed explanation of the Ribbon integrated by RestTemplate of springcloud components. For more information, please follow other related articles on the PHP Chinese website!