1、什么是Feign

Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。

使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。

具体作用体现在

Feign 采用的是基于接口的注解
Feign 整合了ribbon,具有负载均衡的能力
整合了Hystrix,具有熔断的能力

2、Feign远程调用的基本流程

程序启动时,会进行包扫描,扫描所有包下所有@FeignClient注解的类,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。

当远程接口的方法被调用,传入参数,根据参数再应用到请求上,进而转化成真正的 Request 请求。由Proxy代理实例去完成真正的远程访问,并且返回结果。

可以不通过HTTP框架请求,完成远程服务的http调用。

3、Feign工作原理

spring cloud feign启动失败 springcloud feign作用_客户端


PS:(图片来源网上素材)

1、基于面向接口的动态代理方式生成实现类

使用feign ,需要定义接口,接口上需要使用到Http相关的注解,标识HTTP请求参数信息。

Feign通过基于面向接口的动态代理方式生成实现类,将请求调用委托到动态代理实现类上

@FeignClient(value = "service-client",fallback = HelloWorldServiceImpl.class)
public interface HelloWorldService {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String helloWorld(@RequestParam(value = "name") String name);
}

2、根据Contract协议规则,解析接口类的注解信息,解析成内部表现

3、基于 RequestBean,动态生成Request

根据传入的Bean对象和注解信息,从中提取出相应的值,来构造Http Request 对象

4、使用Encoder 将Bean转换成 Http报文正文(消息解析和转码逻辑)

5、拦截器负责对请求和返回进行装饰处理
在请求转换的过程中,Feign 抽象出来了拦截器接口,用于用户自定义对请求的操作,比如,如果希望Http消息传递过程中被压缩,可以定义一个请求拦截器。

6、日志记录

7、基于重试器发送HTTP请求

Feign 内置了一个重试器,当HTTP请求出现IO异常时,Feign会有一个最大尝试次数发送请求

8、发送Http请求

Feign 真正发送HTTP请求是委托给 feign.Client 来做的。

Feign 默认底层通过JDK 的 java.net.HttpURLConnection 实现了feign.Client接口类,在每次发送请求的时候,都会创建新的HttpURLConnection 链接,这也就是为什么默认情况下Feign的性能很差的原因。可以通过拓展该接口,使用Apache HttpClient 或者OkHttp3等基于连接池的高性能Http客户端。

4、Feign性能优化-------GZIP压缩

gzip 介绍:gzip 是一种数据格式,采用用 deflate 算法压缩 data;gzip 是一种流行的文件
压缩算法,应用十分广泛,尤其是在 Linux 平台。
gzip 能力:当 Gzip 压缩到一个纯文本文件时,效果是非常明显的,大约可以减少 70%
以上的文件大小。
gzip 作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可
以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏
览体验外,另一个潜在的好处是 Gzip 与搜索引擎的抓取工具有着更好的关系。例如 Google
就可以通过直接读取 gzip 文件来比普通手工抓取 更快地检索网页。
1.2HTTP 协议中关于压缩传输的规定
第一:客户端向服务器请求中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示,
客户端支持的压缩格式(gzip 或者 deflate),如果不发送该消息头,服务器是不会压缩的。
第二:服务端在收到请求之后,如果发现请求头中含有 Accept-Encoding 字段,并且支
持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带 Content-Encoding:gzip 消
息头,表示响应报文是根据该格式压缩过的。
第三:客户端接收到请求之后,先判断是否有 Content-Encoding 消息头,如果有,按该
格式解压报文。否则按正常报文处理。

在consumer中的application.properties中进行配置

#-----------------------------feign gzip
#配置请求 GZIP 压缩
feign.compression.request.enabled=true
#配置响应 GZIP 压缩
feign.compression.response.enabled=true
#配置压缩支持的 MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
#当请求的数据容量达到多少的时候,使用压缩。默认是2048字节。
feign.compression.request.min-request-size=512
#-----------------------------spring boot gzip
# 是否启用压缩
server.compression.enabled=true
# 哪些客户端发出的请求不压缩,默认是不限制
server.compression.excluded-user-agents=gozilla,traviata
# 配置想压缩的请求/应答数据类型,默认是 text/html,text/xml,text/plain
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
# 执行压缩的阈值,默认为2048
server.compression.min-response-size=512

5、 Feign性能优化-------替换掉HttpURLConnection,使用HTTP连接池提供性能

http

1、HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程: (1)客户与服务器建立连接; (2)客户向服务器提出请求; (3)服务器接受请求,并根据请求返回相应的文件作为应答; (4)客户与服务器关闭连接。 客户与服务器之间的HTTP连接是一种一次性连接,它限制每次连接只处理一个请求,当服务器返回本次请求的应答后便立即关闭连接,下次请求再重新建立连接。
2、HTTP三次握手,四次挥手,Http 连接需要的 3 次握手 4 次分手开销很大。

解决思路

采用 http 连接池,节约了大量的 3 次握手 4 次分手;这样能大大提升吞吐率。

feign 的 http 客户端支持 3 种框架;HttpURLConnection、httpclient、okhttp;默认是
HttpURLConnection

1、传统的 HttpURLConnection 是 JDK 自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,也没有必要自己去管理连接对象。
2、HttpClient 相比传统 JDK 自带的 HttpURLConnection,它封装了访问 http 的请求头,参数,内容体,响应等等;它不仅使客户端发送 HTTP 请求变得容易,而且也方便了开发人员测试接口(基于 Http 协议的),即提高了开发的效率,也方便提高代码的健壮性;另外高并发大量的请求网络的时候,还是用“连接池”提升吞吐量。

1、使用httpclient配置连接池

引入httpclient依赖

<dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-httpclient</artifactId>
      <version>10.1.0</version>
</dependency>

开启feign技术对底层httpclient的依赖

feign:
  httpclient:
    # 让feign使用Apache HTTPClient做请求,而不是默认的urlConnection
    enabled: true
    # feign最大连接数
    max-connections: 200
    # feign单个路径的最大连接数
    max-connections-per-route: 50

2、使用okhttp配置连接池

引入okhttp依赖

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>10.1.0</version>
</dependency>

开启feign技术对底层okhttp的依赖

feign:
  httpclient:
    # feign最大连接数
    max-connections: 200
    # feign单个路径的最大连接数
    max-connections-per-route: 50
  okhttp:
    # 让feign使用Apache okhttp做请求,而不是默认的urlConnection
    enabled: true

6、Feign性能优化-------设置合理的日志级别

在生产环境,需要打印feign的日志,使用basic级别就ok了,强烈不建议使用full。打印日志太多,消耗feign的性能

Feign的四种自定义日志级别

级别

打印内容

适用场景

NONE(默认值)

不记录任何日志

BASIC

仅记录请求方式、url、响应状态码以及执行时间

生产环境

HEADERS

记录BASIC级别基础上,记录请求和响应的hearder

FULL

记录请求和响应的header、body和元数据(全部信息)

开发环境

1、细粒度的配置Feign的日志级别(针对每个微服务配置)

1.1、通过java代码实现

在Feign接口注解上面配置configuration

@FeignClient(value = "service-client",configuration = HelloWorldConfiguration.class)
public interface HelloWorldService {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String helloWorld(@RequestParam(value = "name") String name);
}

定义configuration内容,设置feign的日志级别

PS:在此方法上不需要@Configuration注解,否则会被所有的FeignClient共享,如果添加了注解,则需要将此类放到启动时扫描不到的包

public class HelloWorldConfiguration{

    @Bean
    Logger.Level feignLoggerLevel(){
        // 设置日志
        return Logger.Level.FULL;
    }
}

将Feign的全路径在application.yml中配置

logging:
  level:
    com.yi.service: debug

1.2、通过配置文件实现(针对单个服务日志)

@FeignClient(value = "service-client")
public interface HelloWorldService {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String helloWorld(@RequestParam(value = "name") String name);
}
logging:
  level:
 	com.yi.service: debug

feign:
  client:
    config:
      # 要调用服务的名称
      service-client:
        loggerLevel: full

2、全局日志级别的配置

2.1、通过java代码实现全局日志级别配置

在启动类@EnableFeignClients注解上配置defaultConfiguration

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients(defaultConfiguration = GlobalFeignConfiguration.class)
public class SericeFeignApplication{

    public static void main(String[] args) {
        SpringApplication.run(SericeFeignApplication.class, args);
    }
}

定义GlobalFeignConfiguration类,此类也不需要@Configuration注解

public class GlobalFeignConfiguration {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

配置文件application.yml

logging:
  level:
 	com.yi.service: debug

2.2、通过配置文件方式实现全局日志级别配置

logging:
  level:
     com.yi.service: debug

feign:
  client:
    config:
      # feign全局日志级别
      default:
        loggerLevel: full