Talk about RetryableFeignLoadBalancer of spring cloud.

  feign

Order

This article mainly studies spring cloud’s RetryableFeignLoadBalancer.

RetryableFeignLoadBalancer

spring-cloud-openfeign-core-2.2.0.M1-sources.jar! /org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancer.java

public class RetryableFeignLoadBalancer extends FeignLoadBalancer
        implements ServiceInstanceChooser {

    private final LoadBalancedRetryFactory loadBalancedRetryFactory;

    public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
            ServerIntrospector serverIntrospector,
            LoadBalancedRetryFactory loadBalancedRetryFactory) {
        super(lb, clientConfig, serverIntrospector);
        this.loadBalancedRetryFactory = loadBalancedRetryFactory;
        this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
    }

    @Override
    public RibbonResponse execute(final RibbonRequest request,
            IClientConfig configOverride) throws IOException {
        final Request.Options options;
        if (configOverride != null) {
            RibbonProperties ribbon = RibbonProperties.from(configOverride);
            options = new Request.Options(ribbon.connectTimeout(this.connectTimeout),
                    ribbon.readTimeout(this.readTimeout));
        }
        else {
            options = new Request.Options(this.connectTimeout, this.readTimeout);
        }
        final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory
                .createRetryPolicy(this.getClientName(), this);
        RetryTemplate retryTemplate = new RetryTemplate();
        BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory
                .createBackOffPolicy(this.getClientName());
        retryTemplate.setBackOffPolicy(
                backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
        RetryListener[] retryListeners = this.loadBalancedRetryFactory
                .createRetryListeners(this.getClientName());
        if (retryListeners != null && retryListeners.length != 0) {
            retryTemplate.setListeners(retryListeners);
        }
        retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
                : new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this,
                        this.getClientName()));
        return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
            @Override
            public RibbonResponse doWithRetry(RetryContext retryContext)
                    throws IOException {
                Request feignRequest = null;
                // on retries the policy will choose the server and set it in the context
                // extract the server and update the request being made
                if (retryContext instanceof LoadBalancedRetryContext) {
                    ServiceInstance service = ((LoadBalancedRetryContext) retryContext)
                            .getServiceInstance();
                    if (service != null) {
                        feignRequest = ((RibbonRequest) request
                                .replaceUri(reconstructURIWithServer(
                                        new Server(service.getHost(), service.getPort()),
                                        request.getUri()))).toRequest();
                    }
                }
                if (feignRequest == null) {
                    feignRequest = request.toRequest();
                }
                Response response = request.client().execute(feignRequest, options);
                if (retryPolicy != null
                        && retryPolicy.retryableStatusCode(response.status())) {
                    byte[] byteArray = response.body() == null ? new byte[] {}
                            : StreamUtils
                                    .copyToByteArray(response.body().asInputStream());
                    response.close();
                    throw new RibbonResponseStatusCodeException(
                            RetryableFeignLoadBalancer.this.clientName, response,
                            byteArray, request.getUri());
                }
                return new RibbonResponse(request.getUri(), response);
            }
        }, new LoadBalancedRecoveryCallback<RibbonResponse, Response>() {
            @Override
            protected RibbonResponse createResponse(Response response, URI uri) {
                return new RibbonResponse(uri, response);
            }
        });
    }

    @Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
            FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
        return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(),
                requestConfig);
    }

    @Override
    public ServiceInstance choose(String serviceId) {
        return new RibbonLoadBalancerClient.RibbonServer(serviceId,
                this.getLoadBalancer().chooseServer(serviceId));
    }

}
  • RetryableFeignLoadBalancer inherits FeignLoadBalancer and implements ServiceInstanceChooser interface
  • Its constructor creates defaultloadbalanceerroryhandler according to clientConfig; Its choose method uses getLoadBalancer().chooseServer, and finally returns it through the RibbonLoadBalancerClient. Ribbon Server wrapper.
  • Its execute method first creates LoadBalancedRetryPolicy, then creates retryTemplate, and finally implements the retry function through retryTemplate.execute. The dowithretrymethod of its RetryCallback will switch to the next service i nstance doWithRetry if the retryContext is LoadBalancedRetryContext.

Summary

RetryableFeignLoadBalancer inherits FeignLoadBalancer and uses retryTemplate to retry the execute method, where the next service instance will be switched to retry under the condition that retryContext is LoadBalancedRetryContext

doc