Feign
1 Feign概述
只需要接口加注解即可实现调用远程服务像调用本地服务一样
…
通过创建与provide服务端controller相对应的接口,来简洁的实现 “调用接口方法即向服务端发送请求并利用返回值接收响应” 的操作,消费端只需要通过@EnableFeignClients开启feign接口扫描,为其创建代理对象注入ioc,使用时获取对象并调用其方法即可直接访问服务端
2 Feign使用
-
新建服务:feign-provide
-
pom.xml
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.moyu</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
-
UserController
-
@RestController @RequestMapping("/provide") public class UserController { @Autowired private UserService userService; @GetMapping("/getUserById/{id}") public User getUserById(@PathVariable("id") Integer id) { return userService.getUserById(id); } }
-
-
application.yml
-
server: port: 8085 spring: application: name: feign-provide cloud: nacos: discovery: server-addr: 192.168.40.132:8848
-
-
-
创建接口:feign-interface
-
pom.xml 导入 openfeign依赖
-
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.2.1.RELEASE</version> </dependency> <!--自定义common包存储pojo实体类--> <dependency> <groupId>com.moyu</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
-
定义接口 UserFeign,接口内容与provide的UserController一一对应,包括类注解请求路径和方法请求路径
-
@RequestMapping("/provide") @FeignClient("feign-provide") public interface UserFeign { //@PathVariable("id"),注解必须传参 @GetMapping("/getUserById/{id}") public User getUserById(@PathVariable("id") Integer id); }
-
-
-
创建消费端:feign-customer
-
pom.xml导入自定义的interface包和nacos-discovery依赖
-
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.moyu</groupId> <artifactId>feign-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
-
UserController
-
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserFeign userFeign; @GetMapping("/getUserById/{id}") public User getUserById(@PathVariable("id") Integer id) { return userFeign.getUserById(id); } }
-
-
app,开启Feign接口扫描,注入ioc容器
-
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignConsumerApp { public static void main(String[] args) { SpringApplication.run(FeignConsumerApp.class, args); } }
-
-
-
测试
3. Feign原理
- 扫描 feign 接口,创建代理对象并交给 ioc 容器管理
- 使用@EnableFeignClients注解开启feign接口扫描,FeignClientsRegistrar.registerFeignClients() 扫描所有被 @FeignClient 标记的接口,创建代理类并注入 ioc 容器管理
- 根据 feign 接口上的注解创建 RequestTemplate 类
- 当在controller类中调用 feign 接口对象的方法时,代理对象会调用 invoke() 方法根据接口上面的注解生成 RequestTemplate(url, requestMethod, body)
- 发送请求
- 通过 RequestTemplate 来创建 Request 请求,然后 client() 方法使用Request发送请求
4. Feign的三种传参方式
请求处理流程:前端发送请求 -> 消费端controller层接收请求和参数 -> 调用feign接口方法并传递参数 -> feign接口根据方法上的注解转换为请求,根据方法参数转换为请求参数,向provide服务端发送转换后的请求和参数 -> 服务端controller接收请求及参数,调用service处理并得到返回值,将返回值封装到响应中并返回 -> feign 接口接收响应并进行解析,得到返回值并将其返回给消费端的controller -> 消费端得到返回值,并对其封装返回给前端
4.1 问号“?”传参
-
@RequestMapping("/user/getUserById") public User getUserById(@RequestParam("id") Integer id);
-
消费端 controller 调用:userFeign.getUserById(1)
-
feign发送请求:
http://ip:port/user/getUserById?id=1
到provide服务端 -
@RequestMapping("/deleteBatch") String deleteBatch(@RequestParam("ids") Integer[] ids);
-
消费端 controller 调用:userFeign.deleteBatch(new int[]{1, 2, 4});
-
feign发送请求:
ip:port/user/deleteBatch?ids=1,2,4
到provide服务端
4.2 restful 传参
-
@RequestMapping("/user/getUserById/{id}") public User getUserById(@PathVariable("id") Integer id);
-
消费端 controller 调用:userFeign.getUserById(1)
-
feign发送请求:
ip:port/user/getUserById/1
到provide服务端
4.3 pojo对象传参
-
@RequestMapping("/user/add") public Result add(@RequestBody User user);
-
消费端controller调用:userFeign.add(new User(3, “王二狗”, 16))
-
feign发送请求:
http://ip:port/user/add
并将参数user封装到请求体中发送请求到provide服务端
5. Feign的优化
5.1 开启日志
Feign中的四种日志级别
- NONE【性能最佳,适用于生产】:不记录任何日志(默认值)
- BASIC【适用于生产环境追踪问题】:仅记录请求方法,URL,响应状态码及执行时间
- HEADERS:在BASIC的基础上,记录请求和响应的 header
- FULL【适用于开发及测试环境定位问题:记录请求和响应的 header,body 和元数据
5.1.1 配置文件方式(常用)
# 显示feign日志
feign:
client:
config:
default:
logger-Level: BASIC
# 开启log4j的日志
logging:
level:
com.moyu.feign: debug # 日志只在此包中生效,并且级别是 debug
5.1.2 配置类形式
-
配置文件中开启日志
-
# 开启log4j的日志 logging: level: com.moyu.feign: debug # 日志只在此包中生效,并且级别是 debug
-
-
配置类中开启feign日志
-
全局配置
-
@Configuration public class FeignLog { @Bean public Logger.Level feignLogLevel(){ return Logger.Level.FULL; } }
-
-
局部配置
-
// @Configuration,去掉注解 public class FeignLog { @Bean public Logger.Level feignLogLevel(){ return Logger.Level.FULL; } }
-
@FeignClient(name="feign-provide", path = "/user", configuration = FeignLog.class) public interface UserService { @GetMapping("/get/{id}") public String get(@PathVariable("id") Integer id); }
-
-
5.2 GZIP压缩
GZIP是一种数据格式,采用deflate 压缩算法,纯文本文件压缩后可减少 70% 以上的大小
使用此方式压缩文件减少了网络请求中传输文件的字节数,提升了传输效率,加快了网页加载速度
Feign开启GZIP
项目中启用GZIP(consumer)
server:
port: 82
# 开启浏览器->consumer的gzip压缩
compression:
enabled: true
feign:
compression:
request:
# 开启feign<->provide服务端的gzip压缩
enabled: true
response:
# 开启响应压缩
enabled: true
将feign日志等级调为FULL,即可看到
5.3 Http连接池
每次http请求的建立过程都要经历三次握手和断开连接的四次挥手,效率太低,可创建http连接池,提前创建http请求连接,访问请求时直接从连接池取出请求连接即可进行数据传输,传输完成请求再返回连接池,实现了重复利用,并且提升了效率
引入依赖即可自动创建连接池
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
5.4 Feign的超时时间设置
feign请求超时时间默认为 1s,当加入日志处理时请求超时时间会延长,有些请求可能需要很长时间处理,则需要自定义配置超时时间
去掉feign的日志记录之后,在服务端接收请求后先sleep(1000)再处理,则会抛出超时异常
配置文件配置
-
通过 feign 配置(常用)
-
feign: client: config: feign-provide: # 对应服务提供者 ConnectionTimeout: 5000 #请求连接的超时时间 ReadTimeout: 5000 #请求处理的超时时间
-
-
通过ribbon 配置:
-
ribbon: ConnectionTimeout: 5000 #请求连接的超时时间 ReadTimeout: 5000 #请求处理的超时时间
-
配置类配置
//全局配置
@Configuration
public class FeignConfig{
@Bean
public Request.Options options(){
return new Request.Options(5000, 10000);
}
}