Spring Cloud Feign
Spring Cloud Feign
Feign概述
Feign是一个声明式的Web Service客户端,是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign,可以做到使用HTTP请求访问远程服务,就像是调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
Feign特性:
- 可拔插的注解支持,包括Feign注解和JAX-RS注解
- 支持可拔插的HTTP编码器和解码器
- 支持Hystrix和它的Fallback
- 支持HTTP请求和响应的压缩
Feign工作原理
- 在开发微服务应用时,我们会在主程序入口添加@EnableFeignClinents注解开启对Feign Client扫描加载处理。根据Feign Client的开发规范,定义接口并加上@FeignClient注解
- 当程序启动时,会进行包扫描,扫描所有@FeignClient的注解的类,并将这些信息注入Spring IOC容器中。当定义的Feign接口中的方法被调用时,通过JDK的代理方式,来生成具体的RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequestTemplate对象,该对象封装了HTTP请求需要的全部信息,如请求参数名、请求方法等信息都是在这个过程中确定的
- 然后有RequestTemplate生成Request,然后把Request交给Client去处理,这是指的Client可以是JDK原生的URLConnection、Apache的Http Client,也可以是Okhttp。最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用
Feign的基础功能
FeignClient注解
FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient注解多种目标在接口上。常用属性如下:
- name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
- url:url一般用于调试,可以受动指定@FeignClient嗲用的地址
- decode404:当放生404错误时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException异常
- configuration:Feign配置类,可以定义Feign的Encoder、Decoder、LogLevel、Contract
- fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
- fallbackFactory:工厂类,用于生成fallback类实例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
- path:定义当前FeignClient的统一前缀
Feign开启GZIP压缩
1 | feign: |
开启GZIP压缩之后,Feign之间的调用通过二进制协议进行传输,返回值需要修改为ResponseEntity<byte[]>
才可以正常显示,否则会导致服务之间的调用结果乱码
Feign属性配置
对单个指定特定名称的Feign进行配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16feign:
client:
config:
feignName: #需要配置的FeignName
connectTimout: 5000 #连接超时时间
readTimeout: 5000 #读超时时间设置
loggerLevel: full #配置Feign的日子级别
errorDcoder: com.example.SimpleErrorDecoder #Feign的错误解码器
retryer: com.example.SimpleRetryer #配置重试
requestInterceptors: #配置拦截器
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
encoder: com.example.SimpleEncoder #Feign的编码器
decoder: com.example.SimpleDecoder #Feign的解码器
contrace: com.example.SimpleContract #Feign的Contract配置作用于所有Feign的配置方式
java配置方式:
1
2
3
4
5
6
7
public class ConsumerApplication{
public static void main(String[] args){
SpringApplication.run(ConsumerApplication.class, args);
}
}配置文件方式:
1
2
3
4
5
6
7feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
Feign Client开启日志
- 在application.yml中设置日志输出级别
- 通过java代码的方式在主程序入口类中配置日志Bean,或者在@Configuration配置中去配置
Feign的超时配置
Feign的调用分两层,即Ribbon的调用和Hystrix的调用,高版本的Hystrix默认是关闭的。
1 | ribbon.ReadTimeout: 120000 |
Feign默认Client的替换
Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。可以使用Apache的HTTP Client替换Feign原始的HTTP Client,通过设置连接池、超时时间等对服务之间的调用调优
使用HTTP Client替换Feign默认Client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign的Starter的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 使用Apache HttpClient替换Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.17.0</version>
</dependency>
</dependencies>1
2
3
4
5
6
7
8
9server:
port: 8010
spring:
application:
name: ch4-3-httpclient
feign:
httpclient:
enabled: true使用okhttp替换Feign默认Client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign的Starter的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
</dependencies>1
2
3
4
5
6
7
8
9
10
11
12server:
port: 8011
spring:
application:
name: ch4-3-okhttp
feign:
httpclient:
enabled: false
okhttp:
enabled: true1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class FeignOkHttpConfig {
public okhttp3.OkHttpClient okHttpClient(){
return new okhttp3.OkHttpClient.Builder()
//设置连接超时
.connectTimeout(60, TimeUnit.SECONDS)
//设置读超时
.readTimeout(60, TimeUnit.SECONDS)
//设置写超时
.writeTimeout(60,TimeUnit.SECONDS)
//是否自动重连
.retryOnConnectionFailure(true)
.connectionPool(new ConnectionPool())
//构建OkHttpClient对象
.build();
}
}
Feign的Post和Get的多参数传递
通过实现Feign的RequestInterceptor中的apply方法来进行统一拦截转换处理Feign中的GET方法多参数传递房问题。
1 |
|
Feign调用传递Toke
在进行认证鉴权时,当使用Feign时就会发现外部请求到A服务的时候,A服务是可以拿到Token的,然而当服务使用Feign调用B服务的时候,Token就会丢失,从而认证失败。需要做的就是在Feign调用的时候,向请求头俩面添加需要传递的Token
1 |
|
解决Feign首次请求失败问题
当Feign和Ribbon整合了Hystrix之后,可能出现首次调用失败的问题:
Hystrix默认的超时时间时1S,如果超过这个时间未做出响应,将会进入fallback代码。由于Bean的装配以及懒加载机制等,Feign首次请求会比较慢。如果这个响应时间大于1S,就会出现请求失败的问题。
- 法一:将Hystrix的超时时间改为5S
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
- 法二:禁用Hystrix的超时时间
hystrix.command.default.execution.timeout.enabled: false
- 法三:使用Feign的时候直接关闭Hystrix(不推荐)
feign.hystrix.enabled.false
Feign返回图片流处理方式
通过Feign返回图片一般为字节数组,但因为Controller层的返回值不能直接返回byte,因此需要将Feign的返回值修改为response
Feign的文件上传
可以通过Feign官方提供的feign-form,其中实现了上传需要的Encoder
1 | <!-- Feign文件上传依赖--> |
1 |
|
1 |
|
venus-cloud-feign的使用
- 解决Spring MVC Controller中的方法不支持继承实现Feign接口中方法参数上的注解问题
- Feign不支持GET方法传递POJO的问题