OpenFeign使用及源码分析

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
应用型负载均衡 ALB,每月750个小时 15LCU
简介: OpenFeign使用及源码分析

1.理解(open-Feign是什么)


open-Feign是一个声明式的Http请求客户端,用于快速发起http请求,降低学习成本


在Feign的基础上增加了对SpringMVC 的注解支持


2.简单使用


2.1 引入jar


   org.springframework.cloud

   spring-cloud-starter-openfeign

   2.1.0.RELEASE


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 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

    **/

   @GetMapping("/{productId}")

   public Result 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 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 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 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 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 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 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 loadBalance(Builder builder, FeignContext context, HardCodedTarget 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接口


有两个实现类


DefaultTargeterHystrixTargeter 默认实现是后者


public  T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget 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 target(Target 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 newInstance(Target target) {

   Map nameToHandler = this.targetToHandlersByName.apply(target);

   Map methodToHandler = new LinkedHashMap();

   List 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包


     

           io.github.openfeign

           feign-okhttp

           11.0

     


在看 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 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 需要两个条件



  1. 需要引入我们开始引入的jar包,feign对okHttpClient的适配
  2. 需要在配置文件中配置feign.okhttp.enabled = true


HttpClient的配置同理


6.对比各个客户端

openFeign

RestTemplate

httpClient

OkHttpClient

负载均衡

支持(内嵌Ribbon)

原生不支持(需要引入Ribbon)

不支持

不支持

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
负载均衡 Java API
SpringCloud之OpenFeign介绍案例+相关面试题
OpenFeign是一个声明式的WEB服务客户端,它使WEB服务客户端变得更加容易。具有可插拔的注解支持,SpringCloud中添加了SpringMVC注解的支持。SpringCloud中集成了Ribbon和Eureka,以及SpringCloud LoadBalance,以便在使用Feign时提供负载均衡的HTTP客户端Feign是一个远程调用的组件集成了Ribbon,默认的负载均衡策略是轮询
509 0
|
6月前
|
Dubbo Java 应用服务中间件
Dubbo 第四节: Spring与Dubbo整合原理与源码分析
DubboConfigConfigurationRegistrar的主要作⽤就是对propties⽂件进⾏解析并根据不同的配置项项⽣成对应类型的Bean对象。
152 0
|
负载均衡 Java 网络架构
十六.SpringCloud源码剖析-Feign源码分析
Spring Cloud OpenFeign 对 Netflix Feign 进行了封装,我们通常都使用Spring Cloud OpenFeign作为服务的负载均衡,本文章主要是探讨一下OpenFeign的初始化流程,以及生成代理类注入到Spring的过程
|
缓存 Java C++
Spring5源码 - 01 BeanDefination源码分析
Spring5源码 - 01 BeanDefination源码分析
90 0
|
前端开发 Java 索引
Spring @AliasFor 实现源码分析
前言 上篇 Spring 注解编程模型 有提到,Spring 对 Java 中的注解进行了增强,使用组合注解或者属性别名,可以让注解中的属性值覆盖元注解的属性值,并且不同的属性可以互为别名,这样在使用时只需要指定其中一个属性,其别名值也间接进行了提供。这篇便从源码进行入手,尝试分析其内部的实现。
390 0
Spring @AliasFor 实现源码分析
|
存储 负载均衡 Java
Spring Cloud学习 之 Spring Cloud Ribbon(执行流程源码分析)
Spring Cloud学习 之 Spring Cloud Ribbon(执行流程源码分析)
115 0
Spring Cloud学习 之 Spring Cloud Ribbon(执行流程源码分析)
|
缓存 负载均衡 Dubbo
SpringCloud原理之feign
前言 文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820… 种一棵树最好的时间是十年前,其次是现在
274 0
|
负载均衡 算法 Java
Spring Cloud Ribbon源码分析(一)
Spring Cloud Ribbon源码分析(一)
148 0
Spring Cloud Ribbon源码分析(一)
|
缓存 监控 Java
Spring Cloud Eureka源码分析(上)
Spring Cloud Eureka源码分析(上)
135 0
Spring Cloud Eureka源码分析(上)
|
存储 缓存 Java
Spring Cloud Eureka源码分析(下)
Spring Cloud Eureka源码分析(下)
155 0
Spring Cloud Eureka源码分析(下)