一、初识 Feign
1. 认识 Feign
以前利用 RestTemplate 发送远程调用代码如下
存在下面的问题:
-
代码可读性差,编程体验不统一
-
参数复杂URL难以维护
Feign 是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
2. 使用 Feign
-
在子工程 pom 引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> -
在 order-service 的启动类添加注解开启 Feign 功能
- 编写 Feign 客户端, 创建接口
- 使用 Fegin, 将接口注入进来直接调用方法
到此 Feign 就替代了 RestTempate, 下面是 RestTempate 实现远程调用,是不是上面的更优雅呢。
3. 小结
使用 Feign 步骤
- 子模块引入引入 openfeign 依赖
- 子模块启动类添加 @EnableFeignClients 注解
- 子模块添加接口编写方法
- 使用 Feign 替代 RestTemplate
二、自定义配置
1. Feign 自定义配置介绍
类型 | 作用 | 说明 |
---|---|---|
feign.Loger.Level | 修改日志界别 | 包括四种不同的级别:none、basic、headers、full |
feign.codec.Decoder | 响应结果的解析器 | http 远程调用的结果做解析,如解析json字符串作为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign.Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
日志级别
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
2. 案例
2.1 配置文件方式
-
局部生效,写具体的服务名
feign:
client:
config:
userservice: # 写具体服务名称,针对某个微服务的配置
loggerLevel: FULL # 日志级别 -
全局生效, default
feign:
client:
config:
default: # 使用default为全局配置
loggerLevel: FULL # 日志级别
2.2 java 代码方式
-
声明一个类,然后声明一个 Logger.Level 的对象:
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel() {
return Logger.Level.BASIC; // 日志级别
}
} -
选择生效范围
-
如果局部生效,放在 @FeignClient 注解中
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class)
-
如果是全局生效,放在模块的启动类中的 @EnableFeignClients 注解上
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
三、Feign 使用优化
Feign 底层发起 HTTP 请求是依赖其它框架的,Feign 底层客户端实现如下
- URLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池
- OKHttp:支持连接池
因此优化 Feign 的性能主要包
- 使用连接池代替默认的URLConnection
- 日志级别最好用basic或none
- 引入依赖
在 order-service 的pom文件中引入 apache 的 httpclient
依赖:
<!--引入HttpClient依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
-
配置连接池
feign:
httpclient:
enabled: true # 支持HttpClient的开关
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 单个路径的最大连接数
四、最佳实践
Feign 的 客户端与 Controller 代码很相似,
Feign 客户端代码片段
服务提供者的 Controller 代码片段
下面介绍如何优化这些重复的代码
1. 方式一(继承)
继承: 给消费者的 FeignClient 和 服务提供者 Controller 定义统一的父接口作为标准。但这种方式不太推荐。
缺点:
- 耦合度太高,如果父接口变了,继承它的子类都要变。
- 参数列表中的注解映射并不会继承,因此Controller中必须再次声明方法、参数列表、注解
优点:
实现了代码共享
2. 方式二(抽取)
抽取: 将 FeignClient 抽取为独立模块,并且把接口有关的 POJO、默认的 Feign 配置放在这个模块中,提供给所有的消费者使用。
3. 基于抽取方式的案例
- 抽取:
- 创建一个模块名为 feign-api 的模块
-
在 feign-api 模块的 pom 引入依赖
<!-- feign 依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
把 order-service中编写的UserClient、User、DefaultFeignConfiguration复制到feign-api项目中。
-
把 order-service 中 编写的UserClient、User、DefaultFeignConfiguration 删除,再 order-service 的 pom 文件中 引入 feign-api的依赖
<dependency> <groupId>cn.itcast.demo</groupId> <artifactId>feign-api</artifactId> <version>1.0</version> </dependency> -
修改order-service中的所有与上述三个组件有关的导包部分,改成导入feign-api中的包
- 测试
启动后发现报错了,找不到 UserClient 是什么情况呢?
原来是因为 userClient 在 cn.itcast.client 的包下,而 order-servcie 模块下调用 UserClient 是cn.itcast.order.service 包下,不在同个包下所以没有扫描出来。
有两个解决方案:
**方案1:**启动类中指定Feign应该扫描的包:
@EnableFeignClients(basePackages = "cn.itcast.client")
**方案2:**在启动类中指定需要加载的Client接口字节码:
@EnableFeignClients(clients = {UserClient.class})