好得很程序员自学网

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

SpringCloud Open feign 使用okhttp 优化详解

我就废话不多说了,大家还是直接看代码吧~

<!--web 模块 --> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-web </artifactId> <exclusions> <!--排除tomcat依赖 --> <exclusion> <artifactId> spring-boot-starter-tomcat </artifactId> <groupId> org.springframework.boot </groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-aop </artifactId> </dependency> <!--undertow容器 --> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-undertow </artifactId> </dependency> <!-- feign-okhttp --> <dependency> <groupId> io.github.openfeign </groupId> <artifactId> feign-okhttp </artifactId> </dependency>

配置pom,容器使用undertow,引入feign-okhttp

feign : # Okhttp参数配置 httpclient : enabled : false okhttp : enabled : true max - connections : 200 # 默认值 max - connections - per - route : 50 # 默认值

application.yml文件配置okhttp参数

import feign . Feign ; import okhttp3 . ConnectionPool ; import org . springframework . boot . autoconfigure . AutoConfigureBefore ; import org . springframework . boot . autoconfigure . condition . ConditionalOnClass ; import org . springframework . cloud . openfeign . FeignAutoConfiguration ; import org . springframework . context . annotation . Bean ; import org . springframework . context . annotation . Configuration ; import java . util . concurrent . TimeUnit ; @Configuration @ConditionalOnClass ( Feign . class ) @AutoConfigureBefore ( FeignAutoConfiguration . class ) public class FeignOkHttpConfig { @Bean public okhttp3 . OkHttpClient okHttpClient (){ return new okhttp3 . OkHttpClient . Builder () //设置连接超时 . connectTimeout ( 60 , TimeUnit . SECONDS ) //设置读超时 . readTimeout ( 60 , TimeUnit . SECONDS ) //设置写超时 . writeTimeout ( 120 , TimeUnit . SECONDS ) //是否自动重连 . retryOnConnectionFailure ( true ) . connectionPool ( new ConnectionPool ()) . addInterceptor ( new OkHttpLogInterceptor ()) //构建OkHttpClient对象 . build (); } }

创建FeignOkHttpConfig类文件

import lombok . extern . log4j . Log4j2 ; import okhttp3 . Interceptor ; import okhttp3 . Request ; import okhttp3 . Response ; import okhttp3 . ResponseBody ; import java . io . IOException ; @Log4j2 public class OkHttpLogInterceptor implements Interceptor { @Override public Response intercept ( Interceptor . Chain chain ) throws IOException { //这个chain里面包含了request和response,所以你要什么都可以从这里拿 Request request = chain . request (); long t1 = System . nanoTime (); //请求发起的时间 log . info ( String . format ( "发送请求 %s on %s%n%s" , request . url (), chain . connection (), request . headers ())); Response response = chain . proceed ( request ); long t2 = System . nanoTime (); //收到响应的时间 //这里不能直接使用response.body().string()的方式输出日志 //因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一 //个新的response给应用层处理 ResponseBody responseBody = 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 ())); return response ; } }

创建OkHttpLogInterceptor日志拦截文件

注意FeignOkHttpConfig中添加

@Bean public Contract feignContract () { return new feign . Contract . Default (); }

feigin请求的@PostMapping @GetMapping等会不受支持

图一,使用默认http

图一,Feign通过jdk中的HttpURLConnection

图二,Feign使用okhttp请求

补充:Feign、httpclient、OkHttp3 结合使用

1 Feign 客户端实现 类型

前面介绍到了常用的Feign客户端实现类,大致如下:

(1) Client.Default类:默认的 feign.Client 客户端实现类,内部使用HttpURLConnnection 完成HTTP URL请求处理;

(2) ApacheHttpClient 类:内部使用 Apache httpclient 开源组件完成HTTP URL请求处理的feign.Client 客户端实现类;

(3) OkHttpClient类:内部使用 OkHttp3 开源组件完成HTTP URL请求处理的feign.Client 客户端实现类。

(4) LoadBalancerFeignClient 类:这是一个特殊的 feign.Client 客户端实现类。内部先使用 Ribbon 负载均衡算法计算server服务器,然后使用包装的 delegate 客户端实例,去完成 HTTP URL请求处理。

Feign 在启动的时候,有两个与feign.Client 客户端实例相关的自动配置类,根据多种条件组合,去创建不同类型的 客户端Spring IOC容器实例。

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 的。具体可以参见其源码,节选如下:

import com . 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 实例的负载均衡客户端 }) public class FeignRibbonClientAutoConfiguration { //空的构造器 public FeignRibbonClientAutoConfiguration () { } //…. }

从源码中可以看到,FeignRibbonClientAutoConfiguration 的自动配置有两个前提条件:

(1)当前的类路径中,存在 ILoadBalancer.class 接口

(2)当前的类路径中,存在 Feign.class 接口

在这里,重点说一下 ILoadBalancer.class 接口,该接口处于 ribbon 的jar包中。如果需要在类路径中导入该jar包,则需要在Maven的pom.xml文件中,增加 ribbon 的相关依赖,具体如下:

<!-- ribbon--> <dependency> <groupId> org.springframework.cloud </groupId> <artifactId> spring-cloud-starter-netflix-ribbon </artifactId> </dependency>

为了加深大家对客户端负载均衡的理解,这里将 ILoadBalancer.class 接口的两个重要的抽象方法列出来,具体如下:

package com . netflix . loadbalancer ; import java . util . List ; public interface ILoadBalancer { // 通过负载均衡算法计算server服务器 Server chooseServer ( Object var1 ); // 取得全部的服务器 List < Server > getAllServers (); //… }

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 ) class HttpClientFeignLoadBalancedConfiguration { //空的构造器 HttpClientFeignLoadBalancedConfiguration () { } @Bean @ConditionalOnMissingBean ({ Client . class }) public Client feignClient ( CachingSpringLoadBalancerFactory cachingFactory , SpringClientFactory clientFactory , HttpClient httpClient ) { ApacheHttpClient delegate = new ApacheHttpClient ( httpClient ); return new LoadBalancerFeignClient ( delegate , cachingFactory , clientFactory ); // 进行包装 } //…省略不相干的代码 }

首先,来看源码中的 feignClient(…)方法,分为两步:

(1)创建一个 ApacheHttpClient 类型的 feign.Client客户端实例,该实例的内部使用 Apache httpclient 开源组件完成HTTP URL请求处理;

(2)创建一个 LoadBalancerFeignClient 负载均衡客户端实例,将 ApacheHttpClient 实例包装起来,然后返回LoadBalancerFeignClient 客户端实例,作为 feign.Client 类型的Spring IOC 容器实例。

然后,再看类 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 依赖,具体如下:

<dependency> <groupId> io.github.openfeign </groupId> <artifactId> feign-httpclient </artifactId> <version> 9.5.1 </version> <!--<version>${feign-httpclient.version}</version>--> </dependency> <dependency> <groupId> org.apache.httpcomponents </groupId> <artifactId> httpclient </artifactId> <version> ${httpclient.version} </version> </dependency>

对于 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" ) class OkHttpFeignLoadBalancedConfiguration { //空的构造器 OkHttpFeignLoadBalancedConfiguration () { } @Bean @ConditionalOnMissingBean ({ Client . class }) public Client feignClient ( CachingSpringLoadBalancerFactory cachingFactory , SpringClientFactory clientFactory , HttpClient httpClient ) { OkHttpClient delegate = new OkHttpClient ( httpClient ); return new LoadBalancerFeignClient ( delegate , cachingFactory , clientFactory ); // 进行包装 } //…省略不相干的代码 }

首先,来看源码中的 feignClient(…)方法,分为两步:

(1)创建一个 OkHttpClient 类型的 feign.Client客户端实例,该实例的内部使用 OkHttp3 开源组件完成HTTP URL请求处理;

(2)创建一个 LoadBalancerFeignClient 负载均衡客户端实例,将 OkHttpClient实例包装起来,然后返回LoadBalancerFeignClient 客户端实例,作为 feign.Client 类型的Spring IOC 容器实例。

然后,再看类 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 依赖。具体如下:

<!-- OkHttp --> <dependency> <groupId> com.squareup.okhttp3 </groupId> <artifactId> okhttp </artifactId> </dependency> <!-- feign-okhttp --> <dependency> <groupId> io.github.openfeign </groupId> <artifactId> feign-okhttp </artifactId> </dependency>

对于 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 的源码节选如下:

package org . springframework . cloud . openfeign . ribbon ; //…省略import @Configuration class DefaultFeignLoadBalancedConfiguration { DefaultFeignLoadBalancedConfiguration () { } @Bean @ConditionalOnMissingBean public Client feignClient ( CachingSpringLoadBalancerFactory cachingFactory , SpringClientFactory clientFactory ) { return new LoadBalancerFeignClient ( new Default (( SSLSocketFactory ) null , ( HostnameVerifier ) null ), cachingFactory , clientFactory ); } }

通过源码可以看出,如果前面的两个配置类的条件没有满足,feign.Client 的 IOC 容器实例没有装配,则:

(1) 创建一个 Client.Default 默认客户端实例,该实例的内部,使用HttpURLConnnection 完成URL请求处理;

(2) 创建一个 LoadBalancerFeignClient 负载均衡客户端实例,将 Client.Default 实例包装起来,然后返回LoadBalancerFeignClient 客户端实例,作为 feign.Client 类型的Spring IOC 容器实例。

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

原文链接:https://blog.csdn.net/sunlihuo/article/details/107359375

查看更多关于SpringCloud Open feign 使用okhttp 优化详解的详细内容...

  阅读:28次