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 容器实例。

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

(0)

相关推荐

  • 基于Feign使用okhttp的填坑之旅

    1.由于项目需要远程调用http请求 因此就想到了Feign,因为真的非常的方便,只需要定义一个接口就行. 但是feign默认使用的JDK的URLHttpConnection,没有连接池效率不好,从Feign的自动配置类FeignAutoConfiguration中可以看到Feign除了默认的http客户端还支持okhttp和ApacheHttpClient,我这里选择了okhttp,它是有连接池的. 2.看看网络上大部分博客中是怎么使用okhttp的 1).引入feign和okhttp的mav

  • SpringBoot 配置 okhttp3的操作

    1. Maven 添加依赖 <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency> 2. application.properties 配置文件 ok.http.connect-timeout=30 ok.http.re

  • 使用okhttp替换Feign默认Client的操作

    一 关键pom <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Cloud OpenFeign的Starter的依赖 --> <dependency> &l

  • SpringCloud Open feign 使用okhttp 优化详解

    我就废话不多说了,大家还是直接看代码吧~ <!--web 模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除tomcat依赖 --> <exclusion> <artifactId&

  • SpringCloud Feign远程调用实现详解

    目录 1. Feign远程调用 1.1.Feign替代RestTemplate 1.2.自定义配置 1.2.1.配置文件方式 1.2.2.Java代码方式 2.Feign使用优化 3. 最佳实践 3.1.继承方式 3.2.抽取方式 3.3.实现基于抽取的最佳实践 先来看我们以前利用RestTemplate发起远程调用的代码: 存在下面的问题: 代码可读性差,编程体验不统一 参数复杂URL难以维护 1. Feign远程调用 Feign是一个声明式的http客户端,官方地址:https://gith

  • Spring Cloud Feign高级应用实例详解

    这篇文章主要介绍了Spring Cloud Feign高级应用实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.使用feign进行服务间的调用 Spring boot2X Consul如何使用Feign实现服务调用 2.开启gzip压缩 Feign支持对请求与响应的压缩,以提高通信效率,需要在服务消费者配置文件开启压缩支持和压缩文件的类型 添加配置 feign.compression.request.enabled=true feig

  • SpringCloud微服务熔断器Hystrix使用详解

    目录 什么是Hystrix Hystrix实战 总结 什么是Hystrix 在日常生活用电中,如果我们的电路中正确地安置了保险丝,那么在电压异常升高时,保险丝就会熔断以便切断电流,从而起到保护电路安全运行的作用. 在货船中,为了防止漏水和火灾的扩散,一般会将货仓进行分割,避免了一个货仓出事导致整艘船沉没的悲剧,这就是舱壁保护机制. Hystrix提供的熔断器也类似,在调用某个服务提供者时,当一定时间内请求总数超过配置的阈值,且窗口期内错误率过高,那Hystrix就会对调用请求熔断,后续的请求直接

  • SpringCloud OpenFeign与Ribbon客户端配置详解

    目录 一.前言 二.OpenFeign与Ribbon配置 1.OpenFeign默认处理请求超时时间 1.1.模拟处理请求0.5秒 1.2.模拟处理请求1秒 2.Ribbon配置 2.1.配置请求处理超时5秒 2.2.模拟请求处理5秒 3.OpenFeign配置 3.1.配置请求处理超时6秒 3.2.OpenFeign配置对全部服务有效 3.3.针对某个服务 一.前言 OpenFeign为微服务架构下服务之间的调用提供了解决方案,OpenFeign是一种声明式.模板化的HTTP客户端.在Spri

  • spring Boot与Mybatis整合优化详解

    SpringBoot官方文档http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ 关于spring-boot与mybatis整合优化方面的介绍,就是Mybatis-Spring-boot-starter的介绍: 1.取消spring-mybatis.xml配置 ①自动检测已存在的Datasource 之前,需要在spring-mybatis.xml中配置datasource的Bean,现在只需要在applicat

  • 基于Tomcat安全配置与性能优化详解

    Tomcat 是 Apache软件基金会下的一个免费.开源的WEB应用服务器,它可以运行在 Linux 和 Windows 等多个平台上,由于其性能稳定.扩展性好.免费等特点深受广大用户喜爱.目前,很多互联网应用和企业应用都部署在 Tomcat 服务器上,比如我们公司,哈. 之前我们 tomcat 都采用的是默认的配置,因此在安全方面还是有所隐患的.上周对测试环境的所有服务器的tomcat都做了安全优化,其间也粗略做了一些性能优化,这里就简单记录分享下! 一.版本安全 升级当前的tomcat版本

  • 基于Python中单例模式的几种实现方式及优化详解

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. 比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息.如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪

  • SpringCloud 中使用 Ribbon的方法详解

    在前两章已经给大家讲解了Ribbon负载均衡的规则 以及 如何搭建Ribbon并调用服务,那么在这一章呢 将会给大家说一说如何在SpringCloud中去使用Ribbon.在搭建之前 我们需要做一些准备工作. 1. 搭建Eureka服务器:springCloud-ribbon-server(项目名称) 2. 服务提供者:springCloud-ribbon-police(项目名称) 3. 服务调用者:springCloud-ribbon-person(项目名称) 搭建Eureka服务器 配置 p

  • 基于js文件加载优化(详解)

    在js引擎部分,我们可以了解到,当渲染引擎解析到script标签时,会将控制权给JS引擎,如果script加载的是外部资源,则需要等待下载完后才能执行. 所以,在这里,我们可以对其进行很多优化工作. 放置在BODY底部 为了让渲染引擎能够及早的将DOM树给渲染出来,我们需要将script放在body的底部,让页面尽早脱离白屏的现象,即会提早触发DOMContentLoaded事件. 但是由于在IOS Safari, Android browser以及IOS webview里面即使你把js脚本放到

随机推荐