浅谈SpringCloud feign的http请求组件优化方案
1描述
如果我们直接使用SpringCloudFeign进行服务间调用的时候,http组件使用的是JDK的HttpURLConnection,每次请求都会新建一个连接,没有使用线程池复用。具体的可以从源码进行分析
2源码分析
我们在分析源码很难找到入口,不知道从何开始入手,我们在分析SpringCloudfeign的时候可用在配置文件下面我讲一下个人的思路。
1首先我点击@EnableFeignClients看一下这个注解在哪个资源路径下
如下图所示:
2找到服务启动加载的配置文件
3因为feign底层的负载均衡是基于Ribbon的所以很快就找到了FeignRibbonClientAutoConfiguration.java这个类
@ConditionalOnClass({ILoadBalancer.class,Feign.class}) @Configuration @AutoConfigureBefore(FeignAutoConfiguration.class) @EnableConfigurationProperties({FeignHttpClientProperties.class}) //Orderisimportanthere,lastshouldbethedefault,firstshouldbeoptional //seehttps://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653 @Import({HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class}) publicclassFeignRibbonClientAutoConfiguration{
首先我们从这三个类进行分析,从名字上来看我为了验证没有特殊配置,feign底层走的是不是默认的DefaultFeignLoadBalancedConfiguration.class
OkHttpFeignLoadBalancedConfiguration.class
HttpClientFeignLoadBalancedConfiguration.class
DefaultFeignLoadBalancedConfiguration.class
DefaultFeignLoadBalancedConfiguration.class
@Configuration classDefaultFeignLoadBalancedConfiguration{ @Bean @ConditionalOnMissingBean publicClientfeignClient(CachingSpringLoadBalancerFactorycachingFactory, SpringClientFactoryclientFactory){ returnnewLoadBalancerFeignClient(newClient.Default(null,null), cachingFactory,clientFactory); } }
从上面代码可知每次请求过来都会创建一个新的client,具体的源码演示有兴趣的可以深入研究,在这里不是我们所研究的重点。
OkHttpFeignLoadBalancedConfiguration.class
@Configuration @ConditionalOnClass(OkHttpClient.class) @ConditionalOnProperty(value="feign.okhttp.enabled") classOkHttpFeignLoadBalancedConfiguration{ @Configuration @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) protectedstaticclassOkHttpFeignConfiguration{ privateokhttp3.OkHttpClientokHttpClient; @Bean @ConditionalOnMissingBean(ConnectionPool.class) publicConnectionPoolhttpClientConnectionPool(FeignHttpClientPropertieshttpClientProperties, OkHttpClientConnectionPoolFactoryconnectionPoolFactory){ IntegermaxTotalConnections=httpClientProperties.getMaxConnections(); LongtimeToLive=httpClientProperties.getTimeToLive(); TimeUnitttlUnit=httpClientProperties.getTimeToLiveUnit(); returnconnectionPoolFactory.create(maxTotalConnections,timeToLive,ttlUnit); } @Bean publicokhttp3.OkHttpClientclient(OkHttpClientFactoryhttpClientFactory, ConnectionPoolconnectionPool,FeignHttpClientPropertieshttpClientProperties){ BooleanfollowRedirects=httpClientProperties.isFollowRedirects(); IntegerconnectTimeout=httpClientProperties.getConnectionTimeout(); this.okHttpClient=httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()). connectTimeout(connectTimeout,TimeUnit.MILLISECONDS). followRedirects(followRedirects). connectionPool(connectionPool).build(); returnthis.okHttpClient; } @PreDestroy publicvoiddestroy(){ if(okHttpClient!=null){ okHttpClient.dispatcher().executorService().shutdown(); okHttpClient.connectionPool().evictAll(); } } } @Bean @ConditionalOnMissingBean(Client.class) publicClientfeignClient(CachingSpringLoadBalancerFactorycachingFactory, SpringClientFactoryclientFactory,okhttp3.OkHttpClientokHttpClient){ OkHttpClientdelegate=newOkHttpClient(okHttpClient); returnnewLoadBalancerFeignClient(delegate,cachingFactory,clientFactory); } }
从源码可以看出
1该类是个配置类,当引入OkHttpClient.Class会加载
client方法中可以看出会返回一个http连接池的client HttpClientFeignLoadBalancedConfiguration @Configuration @ConditionalOnClass(ApacheHttpClient.class) @ConditionalOnProperty(value="feign.httpclient.enabled",matchIfMissing=true) classHttpClientFeignLoadBalancedConfiguration{
这个类和OkHttpFeignLoadBalancedConfiguration原理类型
使用OKHttp替代默认的JDK的HttpURLConnection
使用appachhttpclient使用教程类似
使用方法
1pom
io.github.openfeign feign-okhttp
2Yml文件
feign: okhttp: enabled:true
3自定义连接池
可以通过代码进行配置,也可以通过yml配置
@Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) publicclassFeignOkHttpConfig{ @Bean publicokhttp3.OkHttpClientokHttpClient(){ returnnewokhttp3.OkHttpClient.Builder() .readTimeout(60,TimeUnit.SECONDS) .connectTimeout(60,TimeUnit.SECONDS) .connectionPool(newConnectionPool()) .build(); } }
验证
默认的Feign处理会走到如下位置;
位置处于如下图所示
@Override publicResponseexecute(Requestrequest,Optionsoptions)throwsIOException{ HttpURLConnectionconnection=convertAndSend(request,options); returnconvertResponse(connection).toBuilder().request(request).build(); }
走okhttp客户端会走如下代码
具体位置如下图所示:
@OverridepublicResponseexecute()throwsIOException{ synchronized(this){ if(executed)thrownewIllegalStateException("AlreadyExecuted"); executed=true; } captureCallStackTrace(); try{ client.dispatcher().executed(this); Responseresult=getResponseWithInterceptorChain(); if(result==null)thrownewIOException("Canceled"); returnresult; }finally{ client.dispatcher().finished(this); } }
验证结果
如下所示:
彩蛋
okhttp客户端会走的代码可以看出来okhttp有synchronized锁线程安全的那默认的是否是线程安全的呢有待去验证。
追加
如果发现配置的超时时间无效,可以添加以下配置,因为读取超时配置的时候没有读取上面的okhttp的配置参数,而是从Request中读取。
具体配置如下所示:
@Bean publicRequest.Optionsoptions(){ returnnewRequest.Options(60000,60000); }
补充:springCloudfeign使用/优化总结
基于springCloudDalston.SR3版本
1.当接口参数是多个的时候需要指定@RequestParam中的value来明确一下。
/** *用户互扫 *@paramuid被扫人ID *@paramuserId当前用户ID *@return */ @PostMapping(REQ_URL_PRE+"/qrCodeReturnUser") UserQrCodeqrCodeReturnUser(@RequestParam("uid")Stringuid,@RequestParam("userId")IntegeruserId);
2.接口参数为对象的时候需要使用@RequestBody注解并采用POST方式。
3.如果接口是简单的数组/列表参数这里需要使用Get请求才行
@GetMapping(REQ_URL_PRE+"/getUserLevels") MapgetUserLevels(@RequestParam("userIds")List userIds);
4.直接可以在@FeignClient中配置降级处理方式对于一些不重要的业务自定义处理很有帮助
@FeignClient(value="cloud-user",fallback=IUsers.UsersFallback.class)
5.feign默认只有HystrixBadRequestException异常不会走熔断,其它任何异常都会进入熔断,需要重新实现一下ErrorDecoder包装业务异常
示例:https://github.com/peachyy/feign-support
6.feignHTTP请求方式选择
feign默认使用的是基于JDK提供的URLConnection调用HTTP接口,不具备连接池。所以资源开销上有点影响,经测试JDK的URLConnection比ApacheHttpClient快很多倍。但是ApacheHttpClient和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.command.default.execution.isolation.thread.timeoutInMilliseconds=30000可解决这个问题。
负载均衡参数设置
设置了Hystrix的超时参数会还需设置一下ribbon的相关参数这些参数和Hystrix的超时参数有一定的逻辑关系
请求处理的超时时间ribbon.ReadTimeout=120000
请求连接的超时时间ribbon.ConnectTimeout=30000
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。