目录
一、Feign 是什么,有什么用呢?
二、Feign 客户端的使用
2.1、远程调用
1.引入依赖
2.在order-service(发起远程调用的微服务)的启动类添加注解开启Feign的功能
3.编写 Feign 客户端
4.通过 Feign 客户端发起远程调用
2.2、自定义 Feign 配置
1.配置文件方式
2.java代码方式
2.3、Feign 的性能优化
1.引入依赖
2.配置连接池
2.4、Feign 的最佳实践
1.方式一:给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
2.方式二(推荐):将FeignClient抽取为独立模块,并且把接口有关的POJO(实体类)、默认的Feign配置都放到这个模块中,提供给所有消费者使用
一、Feign 是什么,有什么用呢?
以往我们是通过 RestTemplate 发起远程调用,如下
存在问题如下:
- 代码可读性差,编程体验不统一
- 参数复杂URL难以维护
Feign 是一个声明式的 http 客户端,其作用就是用来把我们解决上述问题的~
二、Feign 客户端的使用
2.1、远程调用
主要分为以下步骤:
1.引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.在order-service(发起远程调用的微服务)的启动类添加注解开启Feign的功能
3.编写 Feign 客户端
主要是基于SpringMVC的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Component
@FeignClient("userservice")
public interface UserClients {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
4.通过 Feign 客户端发起远程调用
用 Feign 来代替 RestTemplate 是不是十分优雅~
2.2、自定义 Feign 配置
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下
类型 | 作用 | 说明 |
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
我们最常使用的就是 修改日志级别 ,其他的可以暂时不考虑~
配置Feign日志有以下两种方式:
1.配置文件方式
a、全局生效
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
b、局部生效
feign:
client:
config:
userservice: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
2.java代码方式
首先需要声明一个 Bean,如下
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; //一般使用 BASIC 级别,因为太多的日志信息影响效率
}
}
情况一:如果是全局配置,则把它放到@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
情况二:如果是局部配置,则把它放到@FeignClient这个注解中:
@FeignClient(value = "userservice", configuration = FeignClientConfiguration.class)
2.3、Feign 的性能优化
Feign 的底层客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池
- OKHttp:支持连接池
因此优化Feign的性能主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或none
因此使用 HttpClient 或 OKHttp 代替 URLConnection
1.引入依赖
<!--httpClient的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2.配置连接池
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数
2.4、Feign 的最佳实践
1.方式一:给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
仔细观察我们可以发现, Feign 发起远程调用的接口和接收远程调用请求的 controller 层实现代码是一样的,因此,我们我可以把他们的共性提取出来,写成一个公开的接口,将来我们使用的时候只需要继承这个接口即可,如下图
但这种方式存在一定的问题,以下是官方提出的问题:
也就是说
- 服务紧耦合
- 不适用于 Spring MVC (父接口参数列表中的映射不会被继承)
2.方式二(推荐):将FeignClient抽取为独立模块,并且把接口有关的POJO(实体类)、默认的Feign配置都放到这个模块中,提供给所有消费者使用
具体步骤:
1.首先创建一个module,命名为feign-api,然后引入feign的starter依赖
<!--feign 客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3.在order-service中引入feign-api的依赖
4.修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
5.重启测试
这个时候你重启项目,项目必然会报以下错误
为什么 UserClients 没有对应的对象呢?
UserClients 之前有对象是因为扫描到 @FeignClient 注解注入了对象 ,而现在 order-service 扫描包的范围是启动类下的包,但由于我们刚刚把 UserClients 挪到了 feign-api 这个 Module 中,因此,扫描不到该注解,无法注入对象。
总而言之:当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。
有以下两种解决方式:
方式一:指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
这种方式会将指定包下的所有东西都拿过来。
方式二(推荐):指定FeignClient字节码
@EnableFeignClients(clients = {UserClient.class})
这种方式是精准打击,只拿指定的类,效率上更推荐使用,用哪个,就指定哪个.