天天看點

Open Feign 學習(源碼分析)Open Feign

Open Feign

1.了解(open-Feign是什麼)

open-Feign是一個聲明式的Http請求用戶端,用于快速發起http請求,降低學習成本

在Feign的基礎上增加了對SpringMVC 的注解支援

2.簡單使用

2.1 引入jar

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>
           

2.2 開發服務端,消費端代碼

2.2.1 服務端

package com.zy.more.controller;

import com.zy.more.Page;
import com.zy.more.Result;
import com.zy.more.entity.InventoryDO;
import com.zy.more.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

/**
 * @author: zhangyao
 * @create:2020-06-30 19:07
 **/
@RestController
@RequestMapping("/inventory")
public class InventoryController {
    @Autowired
    InventoryService inventoryService

    @GetMapping("/{productId}")
    public Result<InventoryDO> getInventoryById(@PathVariable("productId") Integer productId){
        return inventoryService.getInventoryById(productId);
    }
}
           

2.2.2 消費端 (開發接口即可)

啟動類增加@EnableFeignClients

package com.zy.more;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author: zhangyao
 * @create:2020-07-01 11:37
 **/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

           

開發接口

package com.zy.more.feign;

import com.zy.more.Result;
import com.zy.more.entity.InventoryDO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

import javax.persistence.GeneratedValue;

/**
 * @author: zhangyao
 * @create:2020-07-01 16:42
 **/
@FeignClient(name = "inventory",path = "/inventory")
public interface InventoryFeign {
    /**
     * 根據商品id查詢庫存資訊
     * @Date: 2020/7/1 17:02
     * @Author: zhangyao
     * @Description:
     * @param productId:
     * @return: com.zy.more.Result<com.zy.more.entity.InventoryDO>
     **/
    @GetMapping("/{productId}")
    public Result<InventoryDO> getInventoryById(@PathVariable("productId") Integer productId);
}

           

2.2.3 解釋

服務端提供服務,正常開發controller即可,無需做任何改動

消費端隻需要加上開發接口,與服務端提供的controller保持一緻,再使用@FeignClient @EnableFeignClients啟用Feign,當調用接口的時候通過@Autowired注入開發的Feign接口來調用

3.原理分析

openFeign最核心的原理是jdk的動态代理

  1. 使用@EnabledFeignClients來啟用Feign,掃描所有的FeignClient
  2. 給每一個FeignClient生成一個動态代理對象的Bean
  3. 當使用這個Bean的時候,在轉換為Request請求發送

是以整個OpenFeign可以了解為兩大部分

  1. 動态代理生成Request請求
  2. 發送Request請求

發送Request請求預設使用java.net包,可以換成okhttp等高可用http用戶端

4.源碼分析

4.1 掃描注冊

通過@EnableFeignClients進入FeignClientRegistrar類,掃描所有的FeignClient,把對應的FeignClient都注冊進Spring容器

FeignClientsRegistrar 注冊類

具體看這兩個方法

//從SpringBoot啟動類進入這裡,掃描所有的FeignClient類
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
    //擷取@EnableFeignClients注解的屬性值 例如basePackage
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
    //如果在@EnableFeignClients中配置了clients,這裡就會加載出來對應的client
        Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
        Object basePackages;
    //如果有對應的client就會去配置這些Client
        if (clients != null && clients.length !=  0) {
            final Set<String> clientClasses = new HashSet();
            basePackages = new HashSet();
            Class[] var9 = clients;
            int var10 = clients.length;

            for(int var11 = 0; var11 < var10; ++var11) {
                Class<?> clazz = var9[var11];
                ((Set)basePackages).add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }

            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            scanner.addIncludeFilter(new FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        } else { //沒有client就走這裡,擷取配置的basePackages
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = this.getBasePackages(metadata);
        }

    	//循環所有的basePackages 掃描每個包下的client
        Iterator var17 = ((Set)basePackages).iterator();

        while(var17.hasNext()) {
            String basePackage = (String)var17.next();
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            Iterator var21 = candidateComponents.iterator();

            while(var21.hasNext()) {
                BeanDefinition candidateComponent = (BeanDefinition)var21.next();
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    //循環每個FeignClient 并擷取對應的屬性
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
                    String name = this.getClientName(attributes);
                    this.registerClientConfiguration(registry, name, attributes.get("configuration"));
                    //把對應的FeignClient 注冊進Spring 容器
                    this.registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }

    }

//配置每個FeignClient的屬性,這裡的屬性就是擷取@FeignClien注解的屬性
    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        this.validate(attributes);
        definition.addPropertyValue("url", this.getUrl(attributes));
        definition.addPropertyValue("path", this.getPath(attributes));
        String name = this.getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = this.getContextId(attributes);
        definition.addPropertyValue("contextId", contextId);
        definition.addPropertyValue("type", className);
        definition.addPropertyValue("decode404", attributes.get("decode404"));
        definition.addPropertyValue("fallback", attributes.get("fallback"));
        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
        definition.setAutowireMode(2);
        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        boolean primary = (Boolean)attributes.get("primary");
        beanDefinition.setPrimary(primary);
        String qualifier = this.getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }
           

4.2 注入調用

當使用@Autowired注入使用FeignClient時,會通過FeignClientFactory工廠類執行個體化一個jdk動态代理傳回,最終通過http用戶端調用Ribbon的負載均衡政策生成Request發送網絡請求

4.2.1 第一步 FeignClientFactory

FeignClientFactory類

實作了FactoryBean,Spring調用getObject的時候傳回一個jdk的動态代理對象

public Object getObject() throws Exception {
        return this.getTarget();
    }

    <T> T getTarget() {
        FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
        Builder builder = this.feign(context);
        //如果feignClient沒有指定url,就使用name作為url
        if (!StringUtils.hasText(this.url)) {
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            } else {
                this.url = this.name;
            }

            this.url = this.url + this.cleanPath();
            //傳回動态代理對象 下文細說
            return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
        } else {//如果有url
            if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
                this.url = "http://" + this.url;
            }

            String url = this.url + this.cleanPath();
            Client client = (Client)this.getOptional(context, Client.class);
            if (client != null) {
                if (client instanceof LoadBalancerFeignClient) {
                    client = ((LoadBalancerFeignClient)client).getDelegate();
                }

                builder.client(client);
            }
			//同樣傳回動态代理對象
            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
        }
    }
           

4.2.2 第二步 Proxy

看一下動态代理對象的生成,通過上文的loadBalance方法

protected <T> T loadBalance(Builder builder, FeignContext context, HardCodedTarget<T> target) {
    Client client = (Client)this.getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        Targeter targeter = (Targeter)this.get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
    } else {
        throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
    }
}
           

我們發現需要首先擷取一個Client對象,這個client對象預設使用的是實作了Feign包下的Client接口的LoadBalanceFeignClient類,這個類預設使用了Ribbon的負載均衡發送請求,發送請求就是調用了這個對象中execute方法(下文詳細介紹),可以替換其他高性能用戶端,再由Feign進行包裝,傳回一個Builder,最終使用Targeter生成動态代理

詳細說明一下Targeter接口

有兩個實作類

DefaultTargeter 和 HystrixTargeter 預設實作是後者

public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
        //如果沒有使用Hystrix 就走這裡
        return feign.target(target);
    } else {
        feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder)feign;
        SetterFactory setterFactory = (SetterFactory)this.getOptional(factory.getName(), context, SetterFactory.class);
        if (setterFactory != null) {
            builder.setterFactory(setterFactory);
        }

        Class<?> fallback = factory.getFallback();
        if (fallback != Void.TYPE) {
            return this.targetWithFallback(factory.getName(), context, target, builder, fallback);
        } else {
            Class<?> fallbackFactory = factory.getFallbackFactory();
            return fallbackFactory != Void.TYPE ? this.targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory) : feign.target(target);
        }
    }
}
           

接着調用feign.target構造Feign對象

Feign類

public <T> T target(Target<T> target) {
    return this.build().newInstance(target);
}

public Feign build() {
    Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy);
    ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
           

可以看到最終target方法調用了Feign類的繼承類ReflectiveFeign的newInstance方法

public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
    //擷取feignClient中的方法
    Method[] var5 = target.type().getMethods();
    int var6 = var5.length;
    //循環Method中的方法,為方法和handler建立對應的映射關系,并放入一個linkedMap
    for(int var7 = 0; var7 < var6; ++var7) {
        Method method = var5[var7];
        if (method.getDeclaringClass() != Object.class) {
            if (Util.isDefault(method)) {
                DefaultMethodHandler handler = new DefaultMethodHandler(method);
                defaultMethodHandlers.add(handler);
                methodToHandler.put(method, handler);
            } else {
                methodToHandler.put(method, (MethodHandler)nameToHandler.get(Feign.configKey(target.type(), method)));
            }
        }
    }

    //建構動态代理需要的InvocationHandler
    InvocationHandler handler = this.factory.create(target, methodToHandler);
    //生成代理對象
    T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
    Iterator var12 = defaultMethodHandlers.iterator();

    while(var12.hasNext()) {
        DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
        defaultMethodHandler.bindTo(proxy);
    }

    return proxy;
}
           

可以看到這裡傳回的就是jdk的一個代理對象,也是我們第二步最終傳回的對象,之後調用的時候就通過這個動态代理對象來調用具體的方法

詳細看一下這個方法

每個method都對應的一個DefaultMethodHandle,DefaultMethodHandler預設實作類是SynchronousMethodHandler

當動态代理對象調用一個接口時,就是調用SynchronousMethodHandler的invoke方法

4.2.3 第三步 調用

當真正發送一個請求過來的時候的流程:

調用SynchronousMethodHandler的invoke方法

//通過這個方法中的executeAndDecode發送請求
public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = this.buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();

        while(true) {
            try {
                return this.executeAndDecode(template);
            } catch (RetryableException var8) {
                RetryableException e = var8;

                try {
                    retryer.continueOrPropagate(e);
                } catch (RetryableException var7) {
                    Throwable cause = var7.getCause();
                    if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
                        throw cause;
                    }

                    throw var7;
                }

                if (this.logLevel != Level.NONE) {
                    this.logger.logRetry(this.metadata.configKey(), this.logLevel);
                }
            }
        }
    }

//發送請求
Object executeAndDecode(RequestTemplate template) throws Throwable {
        Request request = this.targetRequest(template);
        if (this.logLevel != Level.NONE) {
            this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
        }

        long start = System.nanoTime();

        Response response;
        try {
            //此處的client就是上文第二步中我們使用到的LoadBalanceFeignClient對象
            response = this.client.execute(request, this.options);
        } catch (IOException var15) {
            if (this.logLevel != Level.NONE) {
                this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
            }

            throw FeignException.errorExecuting(request, var15);
        }

        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
        boolean shouldClose = true;

        try {
            if (this.logLevel != Level.NONE) {
                response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel, response, elapsedTime);
            }

            if (Response.class == this.metadata.returnType()) {
                Response var18;
                if (response.body() == null) {
                    var18 = response;
                    return var18;
                } else if (response.body().length() != null && (long)response.body().length() <= 8192L) {
                    byte[] bodyData = Util.toByteArray(response.body().asInputStream());
                    Response var20 = response.toBuilder().body(bodyData).build();
                    return var20;
                } else {
                    shouldClose = false;
                    var18 = response;
                    return var18;
                }
            } else {
                Object result;
                Object var10;
                if (response.status() >= 200 && response.status() < 300) {
                    if (Void.TYPE != this.metadata.returnType()) {
                        result = this.decode(response);
                        shouldClose = this.closeAfterDecode;
                        var10 = result;
                        return var10;
                    } else {
                        result = null;
                        return result;
                    }
                } else if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) {
                    result = this.decode(response);
                    shouldClose = this.closeAfterDecode;
                    var10 = result;
                    return var10;
                } else {
                    throw this.errorDecoder.decode(this.metadata.configKey(), response);
                }
            }
        } catch (IOException var16) {
            if (this.logLevel != Level.NONE) {
                this.logger.logIOException(this.metadata.configKey(), this.logLevel, var16, elapsedTime);
            }

            throw FeignException.errorReading(request, response, var16);
        } finally {
            if (shouldClose) {
                Util.ensureClosed(response.body());
            }

        }
    }
           

到這裡我們可以發現,最終發送請求的類就是LoadBalanceFeignClient

4.2.4 第四步 發送請求

發送請求這部分就是ribbon的相關知識了

public Response execute(Request request, Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost);
            IClientConfig requestConfig = this.getClientConfig(options, clientName);
            return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
        } catch (ClientException var8) {
            IOException io = this.findIOException(var8);
            if (io != null) {
                throw io;
            } else {
                throw new RuntimeException(var8);
            }
        }
}
           

5.擴充

5.1擴充用戶端

修改預設的java.net包下的HttpUrlConnection為HttpClient,OkHttpClient

OkHttpClient

引入jar包

<!-- 擴充openFeign httpClient okhttpClient-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>11.0</version>
        </dependency>
           

在看 openFeign 的配置類 FeignAutoConfiguration

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.openfeign;

import feign.Client;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import okhttp3.ConnectionPool;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
    @Autowired(
        required = false
    )
    private List<FeignClientSpecification> configurations = new ArrayList();

    public FeignAutoConfiguration() {
    }

    @Bean
    public HasFeatures feignFeature() {
        return HasFeatures.namedFeature("Feign", Feign.class);
    }

    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }

    @Configuration
    @ConditionalOnClass({OkHttpClient.class})
    @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
    @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
    @ConditionalOnProperty({"feign.okhttp.enabled"})
    protected static class OkHttpFeignConfiguration {
        private okhttp3.OkHttpClient okHttpClient;

        protected OkHttpFeignConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean({ConnectionPool.class})
        public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
            Integer maxTotalConnections = httpClientProperties.getMaxConnections();
            Long timeToLive = httpClientProperties.getTimeToLive();
            TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
            return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
        }

        @Bean
        public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
            Boolean followRedirects = httpClientProperties.isFollowRedirects();
            Integer connectTimeout = httpClientProperties.getConnectionTimeout();
            Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
            this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects).connectionPool(connectionPool).build();
            return this.okHttpClient;
        }

        @PreDestroy
        public void destroy() {
            if (this.okHttpClient != null) {
                this.okHttpClient.dispatcher().executorService().shutdown();
                this.okHttpClient.connectionPool().evictAll();
            }

        }

        @Bean
        @ConditionalOnMissingBean({Client.class})
        public Client feignClient(okhttp3.OkHttpClient client) {
            return new OkHttpClient(client);
        }
    }

    @Configuration
    @ConditionalOnClass({ApacheHttpClient.class})
    @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
    @ConditionalOnMissingBean({CloseableHttpClient.class})
    @ConditionalOnProperty(
        value = {"feign.httpclient.enabled"},
        matchIfMissing = true
    )
    protected static class HttpClientFeignConfiguration {
        private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
        @Autowired(
            required = false
        )
        private RegistryBuilder registryBuilder;
        private CloseableHttpClient httpClient;

        protected HttpClientFeignConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean({HttpClientConnectionManager.class})
        public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) {
            final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
            this.connectionManagerTimer.schedule(new TimerTask() {
                public void run() {
                    connectionManager.closeExpiredConnections();
                }
            }, 30000L, (long)httpClientProperties.getConnectionTimerRepeat());
            return connectionManager;
        }

        @Bean
        public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
            RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(httpClientProperties.getConnectionTimeout()).setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
            this.httpClient = httpClientFactory.createBuilder().setConnectionManager(httpClientConnectionManager).setDefaultRequestConfig(defaultRequestConfig).build();
            return this.httpClient;
        }

        @Bean
        @ConditionalOnMissingBean({Client.class})
        public Client feignClient(HttpClient httpClient) {
            return new ApacheHttpClient(httpClient);
        }

        @PreDestroy
        public void destroy() throws Exception {
            this.connectionManagerTimer.cancel();
            if (this.httpClient != null) {
                this.httpClient.close();
            }

        }
    }

    @Configuration
    @ConditionalOnMissingClass({"feign.hystrix.HystrixFeign"})
    protected static class DefaultFeignTargeterConfiguration {
        protected DefaultFeignTargeterConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new DefaultTargeter();
        }
    }

    @Configuration
    @ConditionalOnClass(
        name = {"feign.hystrix.HystrixFeign"}
    )
    protected static class HystrixFeignTargeterConfiguration {
        protected HystrixFeignTargeterConfiguration() {
        }

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new HystrixTargeter();
        }
    }
}

           

就很明顯了,想要注入okhttpClient 需要兩個條件

Open Feign 學習(源碼分析)Open Feign
  1. 需要引入我們開始引入的jar包,feign對okHttpClient的适配
  2. 需要在配置檔案中配置feign.okhttp.enabled = true

HttpClient的配置同理

6.對比各個用戶端

openFeign RestTemplate httpClient OkHttpClient
負載均衡 支援(内嵌Ribbon) 原生不支援(需要引入Ribbon) 不支援 不支援