zoukankan      html  css  js  c++  java
  • 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.hreadpool.default.coreSize = 10
    hystrix.hreadpool.default.maxQueueSize = 50
    hystrix.hreadpool.default.queueSizeRejectionThreshold = 30

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

  • 相关阅读:
    基于python的socket网络编程
    Python3报错:ModuleNotFoundError: No module named '_bz2'
    机器学习博客网站
    《Linux内核设计与实现》 读书笔记(4)--进程的调度
    k8s 简单入门
    docker 简单入门
    python3 教程
    .toml 文件简介
    编码规范
    python3 基本用法
  • 原文地址:https://www.cnblogs.com/sglx/p/15763612.html
Copyright © 2011-2022 走看看