SpringCloud Open feign 使用okhttp 优化详解
我就废话不多说了,大家还是直接看代码吧~
org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-undertow io.github.openfeign feign-okhttp
配置pom,容器使用undertow,引入feign-okhttp
feign: #Okhttp参数配置 httpclient: enabled:false okhttp: enabled:true max-connections:200#默认值 max-connections-per-route:50#默认值
application.yml文件配置okhttp参数
importfeign.Feign;
importokhttp3.ConnectionPool;
importorg.springframework.boot.autoconfigure.AutoConfigureBefore;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass;
importorg.springframework.cloud.openfeign.FeignAutoConfiguration;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importjava.util.concurrent.TimeUnit;
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
publicclassFeignOkHttpConfig{
@Bean
publicokhttp3.OkHttpClientokHttpClient(){
returnnewokhttp3.OkHttpClient.Builder()
//设置连接超时
.connectTimeout(60,TimeUnit.SECONDS)
//设置读超时
.readTimeout(60,TimeUnit.SECONDS)
//设置写超时
.writeTimeout(120,TimeUnit.SECONDS)
//是否自动重连
.retryOnConnectionFailure(true)
.connectionPool(newConnectionPool())
.addInterceptor(newOkHttpLogInterceptor())
//构建OkHttpClient对象
.build();
}
}
创建FeignOkHttpConfig类文件
importlombok.extern.log4j.Log4j2;
importokhttp3.Interceptor;
importokhttp3.Request;
importokhttp3.Response;
importokhttp3.ResponseBody;
importjava.io.IOException;
@Log4j2
publicclassOkHttpLogInterceptorimplementsInterceptor{
@Override
publicResponseintercept(Interceptor.Chainchain)throwsIOException{
//这个chain里面包含了request和response,所以你要什么都可以从这里拿
Requestrequest=chain.request();
longt1=System.nanoTime();//请求发起的时间
log.info(String.format("发送请求%son%s%n%s",
request.url(),chain.connection(),request.headers()));
Responseresponse=chain.proceed(request);
longt2=System.nanoTime();//收到响应的时间
//这里不能直接使用response.body().string()的方式输出日志
//因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一
//个新的response给应用层处理
ResponseBodyresponseBody=response.peekBody(1024*1024);
log.info(String.format("接收响应:[%s]%n返回json:【%s】%.1fms%n%s",
response.request().url(),
responseBody.string(),
(t2-t1)/1e6d,
response.headers()));
returnresponse;
}
}
创建OkHttpLogInterceptor日志拦截文件
注意FeignOkHttpConfig中添加
@Bean
publicContractfeignContract(){
returnnewfeign.Contract.Default();
}
feigin请求的@PostMapping@GetMapping等会不受支持
图一,使用默认http
图一,Feign通过jdk中的HttpURLConnection
图二,Feign使用okhttp请求
补充:Feign、httpclient、OkHttp3结合使用
1Feign客户端实现类型
前面介绍到了常用的Feign客户端实现类,大致如下:
(1)Client.Default类:默认的feign.Client客户端实现类,内部使用HttpURLConnnection完成HTTPURL请求处理;
(2)ApacheHttpClient类:内部使用Apachehttpclient开源组件完成HTTPURL请求处理的feign.Client客户端实现类;
(3)OkHttpClient类:内部使用OkHttp3开源组件完成HTTPURL请求处理的feign.Client客户端实现类。
(4)LoadBalancerFeignClient类:这是一个特殊的feign.Client客户端实现类。内部先使用Ribbon负载均衡算法计算server服务器,然后使用包装的delegate客户端实例,去完成HTTPURL请求处理。
Feign在启动的时候,有两个与feign.Client客户端实例相关的自动配置类,根据多种条件组合,去创建不同类型的客户端SpringIOC容器实例。
1.1.1配置LoadBalancerFeignClient负载均衡容器实例
Feign有两个与Client相关的自动配置类:
(1)org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration
(2)org.springframework.cloud.openfeign.FeignAutoConfiguration
第一个自动配置类,能够配置具有负载均衡能力的FeignClient容器实例;第二自动配置类,只能配置最原始的FeignClient容器实例。
具备负载均衡能力的FeignClient容器实例,所对应的类型为LoadBalancerFeignClient类型。前面讲到,在SpringCloud中,为了达到高可用,一个微服务至少应该部署两个以上节点,从这个角度来说,LoadBalancerFeignClient容器实例,已经成为事实上的标配。
事实上,第一个自动配置类FeignRibbonClientAutoConfiguration,在容器的装配次序上,是优先于第二个自动配置类FeignAutoConfiguration的。具体可以参见其源码,节选如下:
importcom.netflix.loadbalancer.ILoadBalancer;
//….
@ConditionalOnClass({ILoadBalancer.class,Feign.class})
@Configuration
@AutoConfigureBefore({FeignAutoConfiguration.class})//本配置类具备优先权
@EnableConfigurationProperties({FeignHttpClientProperties.class})
@Import({
HttpClientFeignLoadBalancedConfiguration.class,//配置:包装ApacheHttpClient实例的负载均衡客户端
OkHttpFeignLoadBalancedConfiguration.class,//配置:包装OkHttpClient实例的负载均衡客户端
DefaultFeignLoadBalancedConfiguration.class//配置:包装Client.Default实例的负载均衡客户端
})
publicclassFeignRibbonClientAutoConfiguration{
//空的构造器
publicFeignRibbonClientAutoConfiguration(){
}
//….
}
从源码中可以看到,FeignRibbonClientAutoConfiguration的自动配置有两个前提条件:
(1)当前的类路径中,存在ILoadBalancer.class接口
(2)当前的类路径中,存在Feign.class接口
在这里,重点说一下ILoadBalancer.class接口,该接口处于ribbon的jar包中。如果需要在类路径中导入该jar包,则需要在Maven的pom.xml文件中,增加ribbon的相关依赖,具体如下:
org.springframework.cloud spring-cloud-starter-netflix-ribbon
为了加深大家对客户端负载均衡的理解,这里将ILoadBalancer.class接口的两个重要的抽象方法列出来,具体如下:
packagecom.netflix.loadbalancer;
importjava.util.List;
publicinterfaceILoadBalancer{
//通过负载均衡算法计算server服务器
ServerchooseServer(Objectvar1);
//取得全部的服务器
ListgetAllServers();
//…
}
FeignRibbonClientAutoConfiguration自动配置类,并没有直接配置LoadBalancerFeignClient容器实例,而是使用@Import注解,通过导入其他配置类的方式,完成LoadBalancerFeignClient客户端容器实例的配置。
分别导入了以下三个自动配置类:
(1)HttpClientFeignLoadBalancedConfiguration.class
该配置类,负责配置一个包装ApacheHttpClient实例的LoadBalancerFeignClient负载均衡客户端。
(2)OkHttpFeignLoadBalancedConfiguration.class
该配置类,负责配置一个包装OkHttpClient实例的LoadBalancerFeignClient负载均衡客户端。
(3)DefaultFeignLoadBalancedConfiguration.class
该配置类,负责配置一个包装Client.Default实例的LoadBalancerFeignClient负载均衡客户端。
1.1.2包装ApacheHttpClient实例的负载均衡容器实例
首先来看如何配置一个包装ApacheHttpClient实例的负载均衡容器实例。这个IOC实例的配置,由HttpClientFeignLoadBalancedConfiguration自动配置类完成的,其源码节选如下:
@Configuration
@ConditionalOnClass({ApacheHttpClient.class})
@ConditionalOnProperty(
value={"feign.httpclient.enabled"},
matchIfMissing=true
)
classHttpClientFeignLoadBalancedConfiguration{
//空的构造器
HttpClientFeignLoadBalancedConfiguration(){
}
@Bean
@ConditionalOnMissingBean({Client.class})
publicClientfeignClient(
CachingSpringLoadBalancerFactorycachingFactory,
SpringClientFactoryclientFactory,HttpClienthttpClient)
{
ApacheHttpClientdelegate=newApacheHttpClient(httpClient);
returnnewLoadBalancerFeignClient(delegate,cachingFactory,clientFactory);//进行包装
}
//…省略不相干的代码
}
首先,来看源码中的feignClient(…)方法,分为两步:
(1)创建一个ApacheHttpClient类型的feign.Client客户端实例,该实例的内部使用Apachehttpclient开源组件完成HTTPURL请求处理;
(2)创建一个LoadBalancerFeignClient负载均衡客户端实例,将ApacheHttpClient实例包装起来,然后返回LoadBalancerFeignClient客户端实例,作为feign.Client类型的SpringIOC容器实例。
然后,再看类HttpClientFeignLoadBalancedConfiguration上的两个重要的注解:
(1)@ConditionalOnClass(ApacheHttpClient.class)
(2)@ConditionalOnProperty(value=“feign.httpclient.enabled”,matchIfMissing=true)
这两个条件的含义为:
(1)必须满足ApacheHttpClient.class在当前类路径中存在;
(2)必须满足工程配置文件中feign.httpclient.enabled配置项的值为true;
如果以上两个条件同时满足,则HttpClientFeignLoadBalancedConfiguration自动配置工作就会启动。
如何验证呢?
首先在工程配置文件中,将配置项feign.httpclient.enabled的值,设置为false。
然后,在HttpClientFeignLoadBalancedConfiguration的feignClient(…)方法内的某行打上断点,重新启动项目,注意观察会发现,整个启动过程中,断点没有被命中。
接下来,将配置项feign.httpclient.enabled的值设置为true,再一次启动项目,断点被命中。由此,可以验证HttpClientFeignLoadBalancedConfiguration自动配置类被启动。
为了满足@ConditionalOnClass(ApacheHttpClient.class)的条件要求,由于ApacheHttpClient类的位置处于feign-httpclient相关的jar包中,所以,需要在pom文件加上feign-httpclient以及httpclient组件相关的Maven依赖,具体如下:
io.github.openfeign feign-httpclient 9.5.1 org.apache.httpcomponents httpclient ${httpclient.version}
对于feign.httpclient.enabled配置项设置,根据@ConditionalOnProperty注解的属性matchIfMissing=true可知,这个可以不用配置,在默认的情况下就为true。换句话说,如果不做特别的配置,feign.httpclient.enabled配置项的值,默认为true。
1.1.3包装OkHttpClient实例的负载均衡容器实例
接下来,来看如何配置一个包装OkHttpClient实例的负载均衡容器实例。这个IOC实例的配置,由OkHttpFeignLoadBalancedConfiguration自动配置类完成的,其源码节选如下:
@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnProperty("feign.okhttp.enabled")
classOkHttpFeignLoadBalancedConfiguration{
//空的构造器
OkHttpFeignLoadBalancedConfiguration(){
}
@Bean
@ConditionalOnMissingBean({Client.class})
publicClientfeignClient(
CachingSpringLoadBalancerFactorycachingFactory,
SpringClientFactoryclientFactory,HttpClienthttpClient)
{
OkHttpClientdelegate=newOkHttpClient(httpClient);
returnnewLoadBalancerFeignClient(delegate,cachingFactory,clientFactory);//进行包装
}
//…省略不相干的代码
}
首先,来看源码中的feignClient(…)方法,分为两步:
(1)创建一个OkHttpClient类型的feign.Client客户端实例,该实例的内部使用OkHttp3开源组件完成HTTPURL请求处理;
(2)创建一个LoadBalancerFeignClient负载均衡客户端实例,将OkHttpClient实例包装起来,然后返回LoadBalancerFeignClient客户端实例,作为feign.Client类型的SpringIOC容器实例。
然后,再看类OkHttpFeignLoadBalancedConfiguration上的两个重要的注解:
(1)@ConditionalOnClass(OkHttpClient.class)
(2)@ConditionalOnProperty(“feign.okhttp.enabled”)
这两个条件的含义为:
(1)必须满足OkHttpClient.class在当前类路径中存在;
(2)必须满足工程配置文件中feign.okhttp.enabled配置项的值为true。
如果以上两个条件同时满足,则OkHttpFeignLoadBalancedConfiguration自动配置工作就会启动。
为了满足@ConditionalOnClass(OkHttpClient.class)的条件要求,由于OkHttpClient.class类的位置处于feign-okhttp相关的jar包中,所以,需要在pom文件加上feign-okhttp以及okhttp3相关的Maven依赖。具体如下:
com.squareup.okhttp3 okhttp io.github.openfeign feign-okhttp
对于feign.okhttp.enabled配置项设置,在默认的情况下就为false。也就是说,如果需要使用feign-okhttp,则一定需要做特别的配置,在工程配置文件中,加上feign.okhttp.enabled配置项的值,并且值必须为true。
如果需要使用feign-okhttp,工程配置文件的配置项大致如下:
feign.httpclient.enabled=false feign.okhttp.enabled=true
1.1.4包装Client.Default客户端实例的负载均衡容器实例
最后,来看如何配置一个包装默认Client.Default客户端实例的负载均衡容器实例。这个IOC实例的配置,由DefaultFeignLoadBalancedConfiguration自动配置类所完成的。该配置类,也就是FeignRibbonClientAutoConfiguration配置类通过@import注解所导入的第3个配置类。
DefaultFeignLoadBalancedConfiguration的源码节选如下:
packageorg.springframework.cloud.openfeign.ribbon;
//…省略import
@Configuration
classDefaultFeignLoadBalancedConfiguration{
DefaultFeignLoadBalancedConfiguration(){
}
@Bean
@ConditionalOnMissingBean
publicClientfeignClient(CachingSpringLoadBalancerFactorycachingFactory,
SpringClientFactoryclientFactory)
{
returnnewLoadBalancerFeignClient(
newDefault((SSLSocketFactory)null,
(HostnameVerifier)null),cachingFactory,clientFactory);
}
}
通过源码可以看出,如果前面的两个配置类的条件没有满足,feign.Client的IOC容器实例没有装配,则:
(1)创建一个Client.Default默认客户端实例,该实例的内部,使用HttpURLConnnection完成URL请求处理;
(2)创建一个LoadBalancerFeignClient负载均衡客户端实例,将Client.Default实例包装起来,然后返回LoadBalancerFeignClient客户端实例,作为feign.Client类型的SpringIOC容器实例。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。