Open Feign learning (source code analysis)

Open Feign

1. understand (what is open feign)

Open feign is a declarative HTTP request client, which is used to quickly initiate HTTP requests and reduce learning costs

Added annotation support for SpringMVC on the basis of Feign

2. simple use

2.1 importing jar s

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

2.2 develop service end and consumer end codes

2.2.1 server

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 consumer (just develop the interface)

Add @EnableFeignClients to the startup class

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);
    }
}

Development interface

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 {
    /**
     * Query inventory information by commodity 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 interpretation

The server provides services. The controller can be developed normally without any changes

The consumer only needs to add a development interface, which is consistent with the controller provided by the server, and then use @FeignClient @EnableFeignClients to enable Feign. When calling the interface, inject the developed Feign interface through @Autowired

3. principle analysis

The core principle of openFeign is the dynamic proxy of jdk

  1. Use @EnabledFeignClients to enable Feign and scan all feignclients
  2. Generate a Bean of dynamic proxy object for each FeignClient
  3. When this Bean is used, the Request is sent when it is converted to a Request

So the whole OpenFeign can be understood as two parts

  1. Dynamic proxy generates Request
  2. Send Request

Sending a Request uses Java by default Net package, which can be replaced with highly available http clients such as okhttp

4. source code analysis

4.1 scan registration

Enter the feignclientregister class through @EnableFeignClients, scan all feignclients, and register the corresponding feignclients into the Spring container

FeignClientsRegistrar registration class

See these two methods for details

//Enter here from the SpringBoot startup class and scan all FeignClient classes
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        scanner.setResourceLoader(this.resourceLoader);
    //Get the attribute value of the @EnableFeignClients annotation, such as basePackage
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
    //If clients are configured in @EnableFeignClients, the corresponding clients will be loaded here
        Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
        Object basePackages;
    //If there are corresponding clients, these clients will be configured
        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 { //Go here without a client to get the configured basePackages
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = this.getBasePackages(metadata);
        }

    	//Cycle through all basePackages and scan client s under each package
        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) {
                    //Loop through each FeignClient and get the corresponding properties
                    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"));
                    //Register the corresponding FeignClient into the Spring container
                    this.registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }

    }

//Configure the properties of each FeignClient. The properties here are the properties to obtain the @FeignClien annotation
    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 injection call

When using @Autowired injection to use FeignClient, a jdk dynamic proxy will be instantiated through the FeignClientFactory factory class to return. Finally, the http client will call the Ribbon's load balancing policy to generate a Request and send a network Request

4.2.1 step 1 FeignClientFactory

FeignClientFactory class

Factorybean is implemented. When spring calls getObject, it returns a jdk dynamic proxy object

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

    <T> T getTarget() {
        FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
        Builder builder = this.feign(context);
        //If feignClient does not specify a url, name is used as the 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();
            //Returning a dynamic proxy object will be described in detail below
            return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
        } else {//If there is a 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);
            }
			//Also return the dynamic proxy object
            Targeter targeter = (Targeter)this.get(context, Targeter.class);
            return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
        }
    }

4.2.2 step 2 Proxy

Let's take a look at the generation of dynamic proxy objects through the loadBalance method above

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?");
    }
}

We found that we need to obtain a client object first. By default, this client object uses the LoadBalanceFeignClient class that implements the client interface under Feign package. By default, this class uses the Ribbon's load balancing send request. The send request calls the execute method in this object (described in detail below), which can replace other high-performance clients. Then Feign wraps it and returns a Builder, Finally, use the target to generate dynamic proxy

Specify the target interface

There are two implementation classes

The default implementation of defaulttarget and hystrixtarger is the latter

public <T> T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget<T> target) {
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
        //If you don't use Hystrix, go here
        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);
        }
    }
}

Then call Feign Target construct Feign object

Feign class

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);
}

You can see that the final target method calls the newInstance method of ReflectiveFeign, an inherited class of Feign class

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();
    //Get methods in feignClient
    Method[] var5 = target.type().getMethods();
    int var6 = var5.length;
    //Loop the Method in the Method, establish the corresponding mapping relationship between the Method and the handler, and put it into a 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 required to build dynamic proxy
    InvocationHandler handler = this.factory.create(target, methodToHandler);
    //Generate proxy object
    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;
}

You can see that what is returned here is a proxy object of the jdk, which is also the object finally returned in the second step. Later, when calling, the specific method will be called through this dynamic proxy object

Take a closer look at this method

Each method corresponds to a defaultmethodhandle. The default implementation class of defaultmethodhandler is SynchronousMethodHandler

When a dynamic proxy object calls an interface, it calls the invoke method of SynchronousMethodHandler

4.2.3 step 3 call

The process when a request is actually sent:

invoke method of SynchronousMethodHandler

//Send the request through executeAndDecode in this method
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);
                }
            }
        }
    }

//Send request
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 {
            //The client here is the LoadBalanceFeignClient object we used in step 2 above
            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());
            }

        }
    }

From here, we can find that the class that finally sends the request is LoadBalanceFeignClient

4.2.4 step 4 send request

Sending a request is about 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. extension

5.1 extended client

Modify the default java Net package is HttpClient,OkHttpClient

OkHttpClient

Import jar package

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

Looking at the configuration class FeignAutoConfiguration of openFeign

//
// 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();
        }
    }
}

Obviously, two conditions are required to inject okhttpClient

  1. We need to import the jar package we started to import. feign's adaptation to okHttpClient
  2. Feign needs to be configured in the configuration file Okhttp Enabled = true

The configuration of HttpClient is the same

6. compare each client

openFeign RestTemplate httpClient OkHttpClient
load balancing Support (embedded Ribbon) Native is not supported (ribbon needs to be introduced) Not supported Not supported

Tags: Java Spring Spring Boot

Posted by collamack on Wed, 01 Jun 2022 16:43:29 +0530