Feign源码解析

一、简介

  Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka、Ribbon组合使用以支持负载均衡;可以与hystrix结合实现服务的限流、熔断、降级。

二、@EnableFeignClients

  我们通常使用@EnableFeignClients注解开启feign的功能,那么就先从这个注解入手。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

    String[] value() default {};

    //可以指定具体的包路径
    String[] basePackages() default {};
    
    //指定具体的class
    Class<?>[] basePackageClasses() default {};
    
    //指定配置
    Class<?>[] defaultConfiguration() default {};
    
    //指定feign client 为null通过扫描得到
    Class<?>[] clients() default {};

}

导入了FeignClientsRegistrar,@Import注解的用法这里就不多说了,不懂的自行学习。

class FeignClientsRegistrar
        implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    
    private ResourceLoader resourceLoader;
    private Environment environment;
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        // 注册配置类
        registerDefaultConfiguration(metadata, registry);
        // 注册feign clients
        registerFeignClients(metadata, registry);
    }
}

注册配置类

    private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        // 获取@EnableFeignClients注解的配置信息
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
        //判断是否指定了配置
        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            //注册到容器中
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }
    //注册了FeignClientSpecification的beandefinition到容器中,后续后实例化
    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
        //添加构造方法入参
        builder.addConstructorArgValue(name);
        //添加构造方法入参
        builder.addConstructorArgValue(configuration);
        //通过构造方法,把配置类填充到FeignClientSpecification属性中去
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }

注册feign clients

public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        // 获取扫描classpath下component组件的扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
Set
<String> basePackages; // 获取注解@EnableFeignClients的信息 Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); // 创建过滤器(扫描被@FeignClient注解修饰的类) AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); // @EnableFeignClients的属性clients为空 if (clients == null || clients.length == 0) { // 扫描器增加过滤器 scanner.addIncludeFilter(annotationTypeFilter); // 获取配置的扫描包的路径,如果没配置,默认为启动类的包路径 basePackages = getBasePackages(metadata); } else { final Set<String> clientClasses = new HashSet<>(); basePackages = new HashSet<>(); //把指定了client所在的包放入basePackages for (Class<?> clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } //通过过滤器筛选需要注册的feign client AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() { @Override protected boolean match(ClassMetadata metadata) { String cleaned = metadata.getClassName().replaceAll("\\$", "."); return clientClasses.contains(cleaned); } }; //添加扫描器 scanner.addIncludeFilter( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } for (String basePackage : basePackages) { // Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // 判断注解的类是否是一个接口 AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); //获取feign client的属性 Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); //获取client的名称 顺序为 contextId,name,serviceId String name = getClientName(attributes); //注册每个client的配置信息 registerClientConfiguration(registry, name, attributes.get("configuration")); //注册feign client 就是往注册器中添加了一个bd 后续会实例化成一个bean registerFeignClient(registry, annotationMetadata, attributes); } } } }

注册bd

private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        //生成FeignClientFactoryBean的bd,是一个FactoryBean
        //这也是为什么@FeignClient作用在一个接口上,却还能被其他bean用@Autowired注解引用
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        //属性校验
        validate(attributes);
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = 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(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        //原始类的className
        beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);

        boolean primary = (Boolean) attributes.get("primary");
        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

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

实例化feign client

  feign client的实例化就是通过FeignClientFactoryBean的getObject方法实现的,下面来解析一下

class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }

    <T> T getTarget() {
        // 这个FeignContext在FeignAutoConfiguration配置中已经声明了,所以可以直接用applicationContext获取bean
        FeignContext context = applicationContext.getBean(FeignContext.class);
        //配置feign 的decoder、encoder、retryer、contract、RequestInterceptor等
        //这些有默认配置,在FeignAutoConfiguration及FeignClientsConfiguration中有默认配置
        Feign.Builder builder = feign(context);
        //没有指定url属性
        if (!StringUtils.hasText(url)) {
            //自动拼接缺省的http请求前缀
            if (!name.startsWith("http")) {
                url = "http://" + name;
            }
            else {
                url = name;
            }
            url += cleanPath();
            //直接返回负载均衡client,发送请求时会先经过负载均衡器,再调用实际发送请求的client
            return (T) loadBalance(builder, context,
                    new HardCodedTarget<>(type, name, url));
        }
        if (StringUtils.hasText(url) && !url.startsWith("http")) {
            url = "http://" + url;
        }
        String url = this.url + cleanPath();
     // feign默认集成了ribbon,所以拿到的是LoadBalancerFeignClient Client client
= getOptional(context, Client.class); if (client != null) { if (client instanceof LoadBalancerFeignClient) { // 获取负载均衡中 实际发送发送请求的client client = ((LoadBalancerFeignClient) client).getDelegate(); } if (client instanceof FeignBlockingLoadBalancerClient) { // 获取负载均衡器中 实际发送请求的client client = ((FeignBlockingLoadBalancerClient) client).getDelegate(); } // 不走负载均衡 builder.client(client); } //默认DefaultTargeter,如果使用了Hystrix 此处返回的就是HystrixTargeter Targeter targeter = get(context, Targeter.class); //创建实例 根据Targeter的类型选择创建逻辑 return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url)); } }

DefaultTargeter走的逻辑就是HystrixTargeter的简化逻辑,所以我们直接分析HystrixTargeter就好了

public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        //没有开启hystrix
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            return feign.target(target);
        }
        //如果开启了hystrix
        feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
        //获取实例名称
        String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
                : factory.getContextId();
        SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
        if (setterFactory != null) {
            builder.setterFactory(setterFactory);
        }
        //注解中的fallback
        Class<?> fallback = factory.getFallback();
        if (fallback != void.class) {
            return targetWithFallback(name, context, target, builder, fallback);
        }
        //注解中的FallbackFactory
        Class<?> fallbackFactory = factory.getFallbackFactory();
        if (fallbackFactory != void.class) {
            return targetWithFallbackFactory(name, context, target, builder,
                    fallbackFactory);
        }
        //开启了hystrix,但是没有写fallback或fallbackFactory方法
        return feign.target(target);
    }

这里分成了几种情况,但是逻辑也差不太多,我们以fallback为例分析

private <T> T targetWithFallback(String feignClientName, FeignContext context,
            Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
            Class<?> fallback) {
        //获取fallback类的实例
        T fallbackInstance = getFromContext("fallback", feignClientName, context,
                fallback, target.type());
        //创建feign client实例        
        return builder.target(target, fallbackInstance);
    }
    public <T> T target(Target<T> target, T fallback) {
      // fallback实例被封装成了FallbackFactory
      return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
          .newInstance(target);
    }
    Feign build(final FallbackFactory<?> nullableFallbackFactory) {
      //添加了一个处理器工厂,后续通过工厂的create方法创建handler
      super.invocationHandlerFactory(new InvocationHandlerFactory() {
        @Override
        public InvocationHandler create(Target target,
                                        Map<Method, MethodHandler> dispatch) {
          //如果是没有使用fallback的,nullableFallbackFactory是null
          return new HystrixInvocationHandler(target, dispatch, setterFactory,
              nullableFallbackFactory);
        }
      });
      super.contract(new HystrixDelegatingContract(contract));
      //调用父类的创建方法
      return super.build();
    }
    //创建实例
  public <T> T newInstance(Target<T> target) {
    //获取所有的方法,并为方法创建相应的处理器SynchronousMethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    //遍历放入methodToHandler 方法映射处理器
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    //通过工厂创建处理器
    InvocationHandler handler = factory.create(target, methodToHandler);
    //jdk动态代理创建feign client实例
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

调用逻辑

  上面讲到了feign client的创建,在请求调用的时候,主要的逻辑就是通过InvocationHandler的invoke方法处理请求的。InvocationHandler是根据工厂类创建的,如果你的项目启用了Hystrix那么创建的就是HystrixInvocationHandler,如果没有启用创建的就是FeignInvocationHandler。HystrixInvocationHandler跟FeignInvocationHandler的区别就是在http调用前实现了Hystrix的限流,熔断等逻辑,最后都会调用到SynchronousMethodHandler的invoke方法。我们就以简单的FeignInvocationHandler为例分析一下远程调用过程吧。

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //一些默认方法
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }
      //根据方法获取对应的处理器SynchronousMethodHandler
      return dispatch.get(method).invoke(args);
    }

SynchronousMethodHandler

public Object invoke(Object[] argv) throws Throwable {
    // 根据方法参数构造出请求模板对象
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 可以在方法参数上面指定请求配置对象Options
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        //发送请求并解码
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
            //失败之后重试
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

请求发送

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    // 根据请求模板对象得到请求request对象
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      // 通过具体的client组件去进行网络请求
      // 如果没有指定url,会使用ribbon的LoadBalancerFeignClient
      response = client.execute(request, options);
      // 封装响应对象
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    if (decoder != null)
      //解码器
      return decoder.decode(response, metadata.returnType());

    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    //输出流
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(),
        elapsedTime);
    try {
      if (!resultFuture.isDone())
        throw new IllegalStateException("Response handling not done");
      return resultFuture.join();
    } catch (CompletionException e) {
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
  }

三、自动装配

  自动装配原理spring.factories中写了一些配置类,这个类中实例化了一些组件的必要实例对象,我们看一下

1、FeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
//FeignClient配置文件
@EnableConfigurationProperties({ FeignClientProperties.class,
        FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {

    @Autowired(required = false)
    private List<FeignClientSpecification> configurations = new ArrayList<>();

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

    //feign上下文,可对外提供类似spring上下文的功能
    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
    protected static class HystrixFeignTargeterConfiguration {
        
        //实例化HystrixTargeter
        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new HystrixTargeter();
        }

    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {

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

    }
    
    //httpClient组件
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(ApacheHttpClient.class)
    @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
    @ConditionalOnMissingBean(CloseableHttpClient.class)
    //feign.httpclient.enabled为true的时候生效
    @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;

        @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() {
                @Override
                public void run() {
                    connectionManager.closeExpiredConnections();
                }
            }, 30000, 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();
            }
        }

    }
    
    //OkHttpClient组件
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(OkHttpClient.class)
   //当不存在ILoadBalancer类的时候,因为feign默认集成了ribbon,所以想要生效,必须手动排除掉ribbon的jar包   @ConditionalOnMissingClass(
"com.netflix.loadbalancer.ILoadBalancer") @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) //feign.okhttp.enabled为true的时候生效 @ConditionalOnProperty("feign.okhttp.enabled") protected static class OkHttpFeignConfiguration { private okhttp3.OkHttpClient okHttpClient; @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(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); } } }

通过调用的源码我们可以知道client的远程调用是通过jdk自带的HttpURLConnection进行的,我们可以切换为HttpClient或者OkHttpClient。

切换为HttpClient

            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-httpclient</artifactId>
                <version>10.10.1</version>
            </dependency>

配置文件

feign.httpclient.enabled = true

切换为OkHttpClient

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>10.10.1</version>
</dependency>

配置文件

feign.httpclient.enabled = false
feign.okhttp.enabled = true

超时配置

    @Bean
    @ConditionalOnMissingBean
    public Request.Options feignRequestOptions() {
        return new Request.Options(connectTime, TimeUnit.MILLISECONDS, readTime, TimeUnit.MILLISECONDS, true);
    }

 2、FeignLoadBalancerAutoConfiguration

@ConditionalOnClass(Feign.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
@Import({ HttpClientFeignLoadBalancerConfiguration.class,
        OkHttpFeignLoadBalancerConfiguration.class,
        DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {

}

这里又导入了三个配置类,对应了默认的Client,HttpClient和OkHttpClient,上面的切换的原理正常情况下在这地方生效的。以其中一个HttpClient为例

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancerConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(BlockingLoadBalancerClient loadBalancerClient,
            HttpClient httpClient) {
        ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
        return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
    }

}

导入了HttpClientFeignConfiguration配置类

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
public class HttpClientFeignConfiguration {

    private final Timer connectionManagerTimer = new Timer(
            "FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

    private CloseableHttpClient httpClient;

    @Autowired(required = false)
    private RegistryBuilder registryBuilder;

  //连接池管理器 @Bean @ConditionalOnMissingBean(HttpClientConnectionManager.
class) public HttpClientConnectionManager connectionManager( ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) { final HttpClientConnectionManager connectionManager = connectionManagerFactory .newConnectionManager(httpClientProperties.isDisableSslValidation(),
              //最大连接数,默认200 httpClientProperties.getMaxConnections(),
              //每个管道的最大连接数,默认50 httpClientProperties.getMaxConnectionsPerRoute(),
              //每条连接的最大存活时间,默认900 httpClientProperties.getTimeToLive(),
              //时间单位,默认 秒 httpClientProperties.getTimeToLiveUnit(),
this.registryBuilder); this.connectionManagerTimer.schedule(new TimerTask() { @Override public void run() { connectionManager.closeExpiredConnections(); } }, 30000, httpClientProperties.getConnectionTimerRepeat()); return connectionManager; } @Bean @ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true") public CloseableHttpClient customHttpClient( HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) { HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement() .useSystemProperties(); this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties); return this.httpClient; } @Bean @ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true) public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) { this.httpClient = createClient(httpClientFactory.createBuilder(),
          //连接池管理器 httpClientConnectionManager, httpClientProperties);
return this.httpClient; }   //创建httpClient实例 private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) { RequestConfig defaultRequestConfig = RequestConfig.custom() .setConnectTimeout(httpClientProperties.getConnectionTimeout()) .setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build(); CloseableHttpClient httpClient = builder .setDefaultRequestConfig(defaultRequestConfig) .setConnectionManager(httpClientConnectionManager).build(); return httpClient; } @PreDestroy public void destroy() throws Exception { this.connectionManagerTimer.cancel(); if (this.httpClient != null) { this.httpClient.close(); } } }

四、常用配置

#ribbon组件开启,默认也是true
ribbon.eureka.enabled = true
#请求连接的超时时间
ribbon.ConnectTimeout = 5000
#请求处理的超时时间
ribbon.ReadTimeout = 25000
#同一实例最大重试次数,不包括首次调用。默认值为0
ribbon.MaxAutoRetries = 0
#同一个服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
ribbon.MaxAutoRetriesNextServer = 0
#是否所有操作都允许重试。默认值为false
ribbon.OkToRetryOnAllOperations = false


#hystrix组件的配置
feign.hystrix.enabled = true
#是否开启超时熔断
hystrix.command.default.execution.timeout.enabled = true
#断路器超时设置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 30000
#熔断线程数限制
hystrix.threadpool.default.coreSize = 10
hystrix.threadpool.default.maxQueueSize = 50
hystrix.threadpool.default.queueSizeRejectionThreshold = 30

#切换http-client,okhttp组件
feign.httpclient.enabled = false
feign.okhttp.enabled = true

posted @ 2022-01-05 18:40  上官兰夏  阅读(847)  评论(0编辑  收藏  举报