乐趣区

聊聊spring-cloud的FeignLoadBalancer

本文主要研究一下 spring cloud 的 FeignLoadBalancer

FeignLoadBalancer

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

public class FeignLoadBalancer extends
        AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {

    private final RibbonProperties ribbon;

    protected int connectTimeout;

    protected int readTimeout;

    protected IClientConfig clientConfig;

    protected ServerIntrospector serverIntrospector;

    public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
            ServerIntrospector serverIntrospector) {super(lb, clientConfig);
        this.setRetryHandler(RetryHandler.DEFAULT);
        this.clientConfig = clientConfig;
        this.ribbon = RibbonProperties.from(clientConfig);
        RibbonProperties ribbon = this.ribbon;
        this.connectTimeout = ribbon.getConnectTimeout();
        this.readTimeout = ribbon.getReadTimeout();
        this.serverIntrospector = serverIntrospector;
    }

    @Override
    public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
            throws IOException {
        Request.Options options;
        if (configOverride != null) {RibbonProperties override = RibbonProperties.from(configOverride);
            options = new Request.Options(override.connectTimeout(this.connectTimeout),
                    override.readTimeout(this.readTimeout));
        }
        else {options = new Request.Options(this.connectTimeout, this.readTimeout);
        }
        Response response = request.client().execute(request.toRequest(), options);
        return new RibbonResponse(request.getUri(), response);
    }

    @Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(RibbonRequest request, IClientConfig requestConfig) {if (this.ribbon.isOkToRetryOnAllOperations()) {return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
                    requestConfig);
        }
        if (!request.toRequest().httpMethod().name().equals("GET")) {return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
                    requestConfig);
        }
        else {return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
                    requestConfig);
        }
    }

    @Override
    public URI reconstructURIWithServer(Server server, URI original) {
        URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig,
                this.serverIntrospector, server);
        return super.reconstructURIWithServer(server, uri);
    }

    //......
}
  • FeignLoadBalancer 继承了 AbstractLoadBalancerAwareClient,它的构造器接收 ILoadBalancer、IClientConfig、ServerIntrospector,设置的 retryHandler 为 RetryHandler.DEFAULT
  • 其 execute 方法首先构造 Request.Options,然后通过 request.client().execute 来获取 Response,最后返回 RibbonResponse
  • FeignLoadBalancer 还覆盖了 getRequestSpecificRetryHandler 方法,针对 ribbon.isOkToRetryOnAllOperations() 来构建不同的 RequestSpecificRetryHandler;还覆盖了 reconstructURIWithServer 方法,它使用 RibbonUtils 的 updateToSecureConnectionIfNeeded 来构建 URI

RibbonRequest

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

    protected static class RibbonRequest extends ClientRequest implements Cloneable {

        private final Request request;

        private final Client client;

        protected RibbonRequest(Client client, Request request, URI uri) {
            this.client = client;
            setUri(uri);
            this.request = toRequest(request);
        }

        private Request toRequest(Request request) {
            Map<String, Collection<String>> headers = new LinkedHashMap<>(request.headers());
            return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
                    request.requestBody());
        }

        Request toRequest() {return toRequest(this.request);
        }

        Client client() {return this.client;}

        HttpRequest toHttpRequest() {return new HttpRequest() {
                @Override
                public HttpMethod getMethod() {
                    return HttpMethod
                            .resolve(RibbonRequest.this.toRequest().httpMethod().name());
                }

                @Override
                public String getMethodValue() {return getMethod().name();}

                @Override
                public URI getURI() {return RibbonRequest.this.getUri();
                }

                @Override
                public HttpHeaders getHeaders() {Map<String, List<String>> headers = new HashMap<>();
                    Map<String, Collection<String>> feignHeaders = RibbonRequest.this
                            .toRequest().headers();
                    for (String key : feignHeaders.keySet()) {headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
                    }
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.putAll(headers);
                    return httpHeaders;

                }
            };
        }

        public Request getRequest() {return this.request;}

        public Client getClient() {return this.client;}

        @Override
        public Object clone() {return new RibbonRequest(this.client, this.request, getUri());
        }

    }
  • RibbonRequest 继承了 ClientRequest 实现了 Cloneable 接口,它提供了 toHttpRequest 方法来将 feign 的 Request 转换为 spring 的 HttpRequest

RibbonResponse

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

    protected static class RibbonResponse implements IResponse {

        private final URI uri;

        private final Response response;

        protected RibbonResponse(URI uri, Response response) {
            this.uri = uri;
            this.response = response;
        }

        @Override
        public Object getPayload() throws ClientException {return this.response.body();
        }

        @Override
        public boolean hasPayload() {return this.response.body() != null;
        }

        @Override
        public boolean isSuccess() {return this.response.status() == 200;
        }

        @Override
        public URI getRequestedURI() {return this.uri;}

        @Override
        public Map<String, Collection<String>> getHeaders() {return this.response.headers();
        }

        Response toResponse() {return this.response;}

        @Override
        public void close() throws IOException {if (this.response != null && this.response.body() != null) {this.response.body().close();}
        }

    }
  • RibbonResponse 实现了 IResponse 接口,将 feign 的 Response 适配为 netflix 的 IResponse

小结

  • FeignLoadBalancer 继承了 AbstractLoadBalancerAwareClient,它的构造器接收 ILoadBalancer、IClientConfig、ServerIntrospector,设置的 retryHandler 为 RetryHandler.DEFAULT
  • 其 execute 方法首先构造 Request.Options,然后通过 request.client().execute 来获取 Response,最后返回 RibbonResponse
  • FeignLoadBalancer 还覆盖了 getRequestSpecificRetryHandler 方法,针对 ribbon.isOkToRetryOnAllOperations() 来构建不同的 RequestSpecificRetryHandler;还覆盖了 reconstructURIWithServer 方法,它使用 RibbonUtils 的 updateToSecureConnectionIfNeeded 来构建 URI

doc

  • FeignLoadBalancer
退出移动版