什么是Feign
- Feign是Spring Cloud Netflix组件中的一个轻量级RESTFULL的http服务客户端,实现了负载均衡和Rest调用的开源框架,封装了Ribbon和RestTemplate,实现了webservice的面向接口编程,进一步降低了项目的耦合度。
- Feign内置了Ribbon,用来做客户端负载均衡调用服务注册中心的服务。
- Feign本身并不支持SpringMVC的注解,它有一套自己的注解,为了更方便的使用,Spring Cloud孵化了OpenFeign。
- Feign是一种申明式、模板化的HTTP客户端,可以让提供者无感知,消费者申明一下即可。
- Feign支持的注解和用法参考官方文档:https://github.com/OpenFign/feign或者spring.io官网。
- Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。
Feign解决什么问题
- Feign旨在是编写java http客户端变得更加容易,Feign简化了RestTemplate代码,实现了Ribbon负载均衡,使代码变得更加简洁,也少了客户端调用的代码,使用Feign实现负载均衡是首先方案。只需要你创建一个接口,然后在上面添加注解即可。
- Feign是声明式服务调用组件,其核心就是:像调用本地方法一样调用远程方法,无感知远程HTTP请求。
- 它解决了让开发者调用远程接口就跟调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是HTTP请求。无需关注与远程的交互细节,更无需关注分布式环境开发。
- 它像Dubbo一样,Consumer直接调用Provider接口方法,而不需要通过常规的Http Client构造请求再解析返回数据。
- Feign vs OpenFeign
- OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如果@RequstMapping、@Pathvariable等等。
- OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实效类,实效类中做负载均衡并调用服务。
Feign入门
参考SpringCloud整合Feign(三)
搭建provider、consumer、api接口模块。
Feign使用步骤
- 消费者添加Feign依赖。
- 创建业务接口,添加@FeignClient注解声明需要调用的服务。
- 接口中的抽象方法使用SpringMVC注解配置服务地址及参数。
- 启动类添加@EnableFeignClients注解激活Feign组件,主要加上api接口包路径。
@FeignClient注解
- name:指定Feign Client的名称,如果项目中使用了Ribbon,name属性会作为微服务的名称,用于服务发现。
- url:url一般用于调试,可以手动指定@FeignClient调用的地址。
- decode404:当发生404错误时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException异常。
- configuration:Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract(契约)。
- fallback:定义容错的处理类,当调用远程接口失败或者超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口。
- fallbackFactory:工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
- path:定义当前FeignClient的统一前缀(一般用于服务提供者指定了服务路径前缀,例如:server.servlet.context-path=/test)。
示例:
application.properties
spring.application.name=feign-service
server.servlet.context-path=/feign-test
定义对外接口
@RequestMapping("/api/v1/group")
@FeignClient(value = "feign-service", path = "feign-test")
public interface TestApi {
@ApiOperation("添加素材")
@PostMapping("add")
String add(@RequestBody @Validated String req);
}
Feign负载均衡
搭建负载均衡环境
- 拷贝之前的provider模块,修改模块名称为providerV2,构成两个服务提供者,负载均环境搭建完毕。
- 负载均衡环境下载地址如下:
springcloud-feign使用DEMO
负载均衡用法
- 可以同时开启provider、providerV2两个服务,使用上面的注解,负载均衡策略为轮询策略,两个provider被调用的几率都是50%。
@FeignClient(value = "service-provider", contextId="OpenApi")
- 我们平时测试的时候可能只想指定一个服务被调用,可以通过增加url参数的方式,指定具体的ip地址和端口即可。
@FeignClient(value = "service-provider", contextId="OpenApi", url = "http://192.168.176.1:8080")
Feign请求传参
请求头
使用@RequestHeader注解参数。
GET
使用@PathVariable注解(路径变量)或者@RequestParam注解(问号后面的键值对参数)接收请求参数。
POST
使用@RequestParam注解(请求body中表单格式参数)、@RequestBody注解(请求体中raw类型json等格式的参数)接收请求参数。
Feign性能优化
Gzip压缩
- gzip介绍:gzip是一种数据格式,采用deflate算法压缩数据;gzip是一种流行的文件压缩算法,应用十分广泛,尤其是在linux平台。
- gzip能力:当Gzip压缩一个纯文本文件时,效果时非常明显的,大约可以减少70%以上的文件大小。
- gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip于搜索引擎的抓取工具邮政更好的关系。例如Goole就可以通过直接读取gzip文件来比普通手工抓取更快的检索网页。
下面是百度的请求响应格式示例:
HTTP协议关于压缩传输的规定
- 客户端的服务器请求中带有:Accept-Encoding:gzip,deflate字段,向服务器表示客户端支持的压缩格式(gzip或者deflate),如果不发生该消息头,服务端默认是不会压缩的。
- 服务端在收到请求后,如果发现请求头中含有Accept-Encoding:gzip消息头,表示服务端要根据该格式对响应报文进行压缩,压缩后返回给客户端并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式进行压缩的。
- 客户端接收到响应后,先判断响应头是否有Content-Encoding消息头,如果有,按照该格式解压报文,否则按正常报文处理。
Gzip压缩案例
局部压缩
只配置Consumer通过Feign到Provider的请求于响应的Gzip压缩。
消费者application.properties的配置
# Feign gzip压缩
# 请求开启gzip压缩
feign.compression.request.enabled=true
# 配置压缩支持的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置压缩数据大小的最小阈值,默认2048
feign.compression.request.min-request-size=512
# 响应开启gzip压缩
feign.compression.response.enabled=true
全局压缩
对客户浏览器的请求已经Consumer对Provider的请求与响应都是些Gzip压缩。
消费者application.properties配置
# 全局gzip压缩
# 开启压缩
server.compression.enabled=true
# 配置压缩支持的MIME TYPE
server.compression.mime-types=application/json,application/xml,text/xml,text/plain
HTTP连接池
为什么HTTP连接池能提升性能?
HTTP的背景原理
- 两台服务器建立HTTP连接的过程是很复杂的,涉及到多个数据包的交换,很耗时。
- HTTP连接需要的3次握手4次挥手开销很大,这一开销对于大量的比较小的HTTP消息来说更大。
解决方案
采用http连接池,可以节约大量的3次握手4次挥手,这样能大大提升吞吐量。
Feign的HTTP客户端支持3中框架:HttpURLConnection、HttpClient、OkHttp;默认是HttpURLConnection。可以通过查看源码org.springframework.cloud.openfeign.ribbo.FeignRibbonClientAutoConfiguration.java得知。
- 传统的HttpURLConnection是JDK自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。对于网络请求这种底层相对复杂的操作,如果有可用的其他方案,没有必要自己去管理连接对象。
- httpClient相比传统JDK自带的HttpURLConnection,它封装了访问HTTP的请求头,参数,内容体,响应等等;它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于HTTP协议的),既提高了开发的效率,又提高了代码的健壮性;另外高并发大量的请求网络的时候,也是用“连接池”提升吞吐量。
HttpClient
将Feign的Http客户端工具修改为HttpClient。
添加依赖
修改Consumer项目,添加两个依赖,我们使用的SpringCloud版本已经默认集成了apache httpclient依赖,所以只需要添加一个依赖即可。
<!-- 当前版本已经默认集成了 apache httpclient依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- feign apache httpclient 依赖 -->
<dependency>
<groupId>io.gi