本文主要研究一下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