为什么要用?
问: 使用了ribbon(负载均衡)后,有没有什么问题?不方便的地方呢?
答: 使用ribbon后,还是需要拼接请求路劲,如果请求参数有多个的话,这时拼接请求字符串就会效率低下,
而feign就很好的解决了此问题
什么是feign?
Feign是Spring Cloud提供的声明式、模板化的HTTP客户端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
由于Netflix为我们提供的框架:Feign,已经闭源了,但是Spring Cloud推出了一款新的框架openfeign
Spring Cloud集成Feign并对其进行了增强,使Feign支持了Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认实现了负载均衡的效果。
如何使用?
由于feign接口是可复用的,所以我们可以将其抽离出来单独编写,减少代码的冗余
-
创建feign-consumer模块(服务消费者)
-
引入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--后面编写的处理服务接口--> <dependency> <groupId>com.bjpowernode</groupId> <artifactId>feign_interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
-
配置文件
server: port: 80 spring: cloud: nacos: discovery: server-addr: 192.168.255.132:8848 #注册中心的地址 application: name: feign-consumer #注册到注册中心的名字
-
模拟请求
@RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private UserFeign userFeign;//代理类 @RequestMapping("/getUserById/{id}") public User getUserById(@PathVariable Integer id){ System.out.println(userFeign.getClass()); return userFeign.getUserById(id); } @RequestMapping("/deleteUserById") public User deleteUserById(Integer id){ return userFeign.deleteUserById(id); } @RequestMapping("/addUser") public User addUser(User user){ return userFeign.addUser(user); } }
-
-
创建feign-provider模块(服务提供者)
-
引入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--后面编写的处理服务接口--> <dependency> <groupId>com.bjpowernode</groupId> <artifactId>feign_interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
-
配置文件
server: port: 9090 spring: cloud: nacos: discovery: server-addr: 192.168.255.132:8848 #nacos服务的地址 application: name: feign-provider #向注册中心注册的名字
-
模拟响应服务
@Service public class UserServiceImpl implements UserService { @Override public User getUserById(Integer id) { return new User(id,"User-1",18); } @Override public User deleteUserById(Integer id) { return new User(id,"删除了User-1",18); } @Override public User addUser(User user) { user.setName("新增了User-1"); return user; } }
-
-
创建feign_interface模块
-
引入依赖
<dependencies> <!--Spring Cloud OpenFeign Starter --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--工具模块--> <dependency> <groupId>com.bjpowernode</groupId> <artifactId>springcloud_common</artifactId> </dependency> </dependencies>
-
编写一个接口在feign包下
@FeignClient("feign-provider") @RequestMapping("/provider") public interface UserFeign { @RequestMapping("/getUserById/{id}")//拼接url public User getUserById(@PathVariable("id") Integer id);//restful形式拼接参数 @RequestMapping("/deleteUserById")//拼接url User deleteUserById(@RequestParam("id") Integer id);//?形式拼接参数,?id=250 @RequestMapping("/addUser") User addUser(@RequestBody User user);//pojo--->json }
-
注解
消费者中:
@EnableFeignClients//开启feign注解的扫描
为什么写在了feign-consumer类的启动类上,而不是feign_interface类中?
因为在feign-consumer中引入了feign_interface,这就相当于把feign_interface中的代码写入了feign-consumer,所有直接在feign-consumer中使用,连同feign_interface中也使用了此注解
feign_interface接口中:
在接口中我们看到有一些熟悉和陌生的注解,但在接口中的注解都和原先的意思有所不同
-
在类上
@FeignClient(“feign-provider”)
处理此类请求的服务名
-
在方法上
以下注解含义仅在使用了@FeignClient注解后表示,并要将参数明文指定出来
-
@RequestMapping
拼接请求路径
注:在
-
@PathVariable
路径方式发送请求参数
-
@RequestParam
?拼接方式发送请求参数
-
@RequestBody
将对象转化为Json串形式发送请求参数
如:实体类(pojo),数组,集合
-
feign实现原理
1、将feign接口的代理类扫描到Spring容器中:
@EnableFeignClients开启feign注解扫描:FeignClientsRegistrar.registerFeignClients()扫描被 @FeignClient标识的接口生成代理类,
并把接口和代理类交给Spring的容器管理。
2、为接口的方法创建RequestTemplate
当consumer调用feign代理类时,代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url,参数)
3、发出请求
代理类会通过RequestTemplate创建Request,然后client(URLConnetct、HttpClient、OkHttp)使用Request发送请求
feign优化
feign发送的是Http请求,实际不如dubbox发送请求快,那我们为什么还要用用feign而不用dubbox呢?
原因就是feign是可以优化滴!
那么都有哪些可以优化的呢?
有以下四点可以优化:
由于feign_interface没有配置文件,全部在feign-consumer配置文件中设置,原因是feign-consumer引入了feign_interface,在feign-consumer配置,等同于在feign_interface中配置,即在feign-consumer配置文件中修改即可
1、开启feign日志
开启feign日志时,还需开启系统日志!
feign的日志级别一共有四种:
-
NONE:没有日志记录
-
BASIC:记录请求方法、URL以及响应状态代码和执行时间
-
HEADERS:记录基本信息以及请求和响应头信息
-
FULL:记录基本信息以及请求和响应头信息、请求和响应体信息
feign:
client:
config:
feign-provider: #开启日志的服务 default:表示所有服务
loggerLevel: full #feign日志的级别
logging:
level:
com.bjpowernode.feign: debug #设置单个包下的日志输出级别
效果如下
2、feign超时优化
处理请求,当某段代码负载过高,运行超时,导致调用超时异常,我们可以通过延长请求连接时间进行优化
1、方式一:
设置ribbon连接请求与处理请求超时时间,单位为毫秒
ribbon:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
2、方式二:
设置feign连接请求与处理请求超时时间,单位为毫秒
feign:
client:
config:
feign-provider:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
3、http连接池
两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间(三次握手四次分手)。采用HTTP连接池可以节约大量的时间。
Feign的HTTP客户端支持3种框架:HttpURLConnection、HttpClient、OkHttp。
Feign 默认是采用HttpURLConnection ,每次请求都会建立、关闭连接。
引入依赖即可,默认设置
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
4、gzip压缩
gzip 是一种数据格式,使用 Gzip 压缩一个纯文本文件时,大约可以减少 70% 以上的文件大小。
server:
port: 80
compression:
enabled: true #开启gzip压缩
默认支持格式
{"text/html", "text/xml", "text/plain", "text/css", "text/javascript", "application/javascript", "application/json", "application/xml"}