好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

浅谈SpringCloud feign的http请求组件优化方案

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测试数据/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 ); } }

验证结果

如下所示:

彩蛋

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

追加

如果发现配置的超时时间无效,可以添加以下配置,因为读取超时配置的时候没有读取上面的okhttp的配置参数,而是从Request中读取。

具体配置如下所示:

@Bean public Request . Options options (){ return new Request . Options ( 60000 , 60000 ); }

补充:springCloud feign使用/优化总结

基于springCloud Dalston.SR3版本

1.当接口参数是多个的时候 需要指定@RequestParam 中的value来明确一下。

/** * 用户互扫 * @param uid 被扫人ID * @param userId 当前用户ID * @return */ @PostMapping ( REQ_URL_PRE + "/qrCodeReturnUser" ) UserQrCode qrCodeReturnUser ( @RequestParam ( "uid" ) String uid , @RequestParam ( "userId" ) Integer userId );

2.接口参数为对象的时候 需要使用@RequestBody注解 并采用POST方式。

3.如果接口是简单的数组/列表参数 这里需要使用Get请求才行

@GetMapping ( REQ_URL_PRE + "/getUserLevels" ) Map < Integer , UserLevel > getUserLevels ( @RequestParam ( "userIds" ) List < Integer > userIds );

4.直接可以在@FeignClient中配置降级处理方式 对于一些不重要的业务 自定义处理很有帮助

@FeignClient ( value = "cloud-user" , fallback = IUsers . UsersFallback . class )

5.feign默认只有HystrixBadRequestException异常不会走熔断,其它任何异常都会进入熔断,需要重新实现一下ErrorDecoder包装业务异常

示例: https://github测试数据/peachyy/feign-support

6. feign HTTP请求方式选择

feign默认使用的是基于JDK提供的URLConnection调用HTTP接口,不具备连接池。所以资源开销上有点影响,经测试JDK的URLConnection比Apache HttpClient快很多倍。但是Apache HttpClient和okhttp都支持配置连接池功能。具体选择需要权衡

7.默认不启用hystrix 需要手动指定feign.hystrix.enabled=true 开启熔断

8.启用压缩也是一种有效的优化方式

feign . compression . request . enabled = true feign . compression . response . enabled = true feign . compression . request . mime - types = text / xml , application / xml , application / json

9.参数相关调优

hystrix线程数设置

设置参数hystrix.threadpool.default.coreSize 来指定熔断隔离的线程数 这个数需要调优,经测试 线程数我们设置为和提供方的容器线程差不多,吞吐量高许多。

第一次访问服务出错的问题

启用Hystrix后,很多服务当第一次访问的时候都会失败 是因为初始化负载均衡一系列操作已经超出了超时时间了 默认的超时时间为1S,设置参数超时时间hystrix测试数据mand.default.execution.isolation.thread.timeoutInMilliseconds=30000 可解决这个问题。

负载均衡参数设置

设置了Hystrix的超时参数会 还需设置一下ribbon的相关参数 这些参数和Hystrix的超时参数有一定的逻辑关系

请求处理的超时时间 ribbon.ReadTimeout=120000

请求连接的超时时间 ribbon.ConnectTimeout=30000

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/qq_33249725/article/details/88532160

查看更多关于浅谈SpringCloud feign的http请求组件优化方案的详细内容...

  阅读:46次