美文网首页
SpringCloud feign的http请求组件优化

SpringCloud feign的http请求组件优化

作者: SuperEngCoding | 来源:发表于2019-03-13 16:21 被阅读0次

    1 描述

    • 如果我们直接使用SpringCloud Feign进行服务间调用的时候,http组件使用的是JDK的HttpURLConnection,每次请求都会新建一个连接,没有使用线程池复用。具体的可以从源码进行分析

    2 源码分析

    我们在分析源码很难找到入口,不知道从何开始入手,我们在分析SpringCloud feign的时候可用在配置文件下面我讲一下个人的思路。

    • 1 首先我点击@EnableFeignClients 看一下这个注解在哪个资源路径下
      如下图所示:


      在这里插入图片描述
    • 2 找到服务启动加载的配置文件


      在这里插入图片描述
    • 3 因为feign底层的负载均衡是基于Ribbon的所以很快就找到了FeignRibbonClientAutoConfiguration.java 这个类
    @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
    @Configuration
    @AutoConfigureBefore(FeignAutoConfiguration.class)
    @EnableConfigurationProperties({ FeignHttpClientProperties.class })
    //Order is important here, last should be the default, first should be optional
    // see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
    @Import({ HttpClientFeignLoadBalancedConfiguration.class,
            OkHttpFeignLoadBalancedConfiguration.class,
            DefaultFeignLoadBalancedConfiguration.class })
    public class FeignRibbonClientAutoConfiguration {
    

    首先我们从这三个类进行分析,从名字上来看我为了验证没有特殊配置,feign底层走的是不是默认的DefaultFeignLoadBalancedConfiguration.class

    • OkHttpFeignLoadBalancedConfiguration.class
    • HttpClientFeignLoadBalancedConfiguration.class
    • DefaultFeignLoadBalancedConfiguration.class

    DefaultFeignLoadBalancedConfiguration.class

    @Configuration
    class DefaultFeignLoadBalancedConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                                  SpringClientFactory clientFactory) {
            return new LoadBalancerFeignClient(new Client.Default(null, null),
                    cachingFactory, clientFactory);
        }
    }
    

    从上面代码可知每次请求过来都会创建一个新的client,具体的源码演示有兴趣的可以深入研究,在这里不是我们所研究的重点。

    OkHttpFeignLoadBalancedConfiguration.class

    @Configuration
    @ConditionalOnClass(OkHttpClient.class)
    @ConditionalOnProperty(value = "feign.okhttp.enabled")
    class OkHttpFeignLoadBalancedConfiguration {
    
        @Configuration
        @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
        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();
                this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
                        connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
                        followRedirects(followRedirects).
                        connectionPool(connectionPool).build();
                return this.okHttpClient;
            }
    
            @PreDestroy
            public void destroy() {
                if(okHttpClient != null) {
                    okHttpClient.dispatcher().executorService().shutdown();
                    okHttpClient.connectionPool().evictAll();
                }
            }
        }
    
        @Bean
        @ConditionalOnMissingBean(Client.class)
        public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                                  SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
            OkHttpClient delegate = new OkHttpClient(okHttpClient);
            return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
        }
    }
    

    从源码可以看出

    • 1 该类是个配置类,当引入OkHttpClient.Class会加载
    • client方法中可以看出会返回一个http连接池的client

    HttpClientFeignLoadBalancedConfiguration

    @Configuration
    @ConditionalOnClass(ApacheHttpClient.class)
    @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
    class HttpClientFeignLoadBalancedConfiguration {
    

    这个类和OkHttpFeignLoadBalancedConfiguration原理类型

    使用OKHttp替代默认的JDK的HttpURLConnection

    • 使用appach httpclient使用教程类似

    使用方法

    • 1 pom
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-okhttp</artifactId>
            </dependency>
    
    • 2 Yml文件
    feign:
      okhttp:
        enabled: true
    
    • 3 自定义连接池
      可以通过代码进行配置,也可以通过yml配置
    @Configuration
    @ConditionalOnClass(Feign.class)
    @AutoConfigureBefore(FeignAutoConfiguration.class)
    public class FeignOkHttpConfig {
        @Bean
        public okhttp3.OkHttpClient okHttpClient(){
            return new okhttp3.OkHttpClient.Builder()
                    .readTimeout(60,TimeUnit.SECONDS)
                    .connectTimeout(60,TimeUnit.SECONDS)
                    .connectionPool(new ConnectionPool())
                    .build();
        }
    }
    
    

    验证

    • 默认的Feign处理会走到如下位置;
      位置处于如下图所示


      在这里插入图片描述
     @Override
        public Response execute(Request request, Options options) throws IOException {
          HttpURLConnection connection = convertAndSend(request, options);
          return convertResponse(connection).toBuilder().request(request).build();
        }
    
    • 走okhttp客户端会走如下代码
      具体位置如下图所示:


      在这里插入图片描述
    @Override public Response execute() throws IOException {
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already Executed");
          executed = true;
        }
        captureCallStackTrace();
        try {
          client.dispatcher().executed(this);
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } finally {
          client.dispatcher().finished(this);
        }
      }
    

    验证结果

    如下所示:


    5457aa9f869ed2f123267b0077e156c5.png

    彩蛋

    • okhttp客户端会走的代码可以看出来okhttp有synchronized锁线程安全的那默认的是否是线程安全的呢 有待去验证。

    相关文章

      网友评论

          本文标题:SpringCloud feign的http请求组件优化

          本文链接:https://www.haomeiwen.com/subject/emdtmqtx.html