什么是Feign?
Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。它将微服务之间的调用变得更简单了,类似于controller调用service。
我们调用微服务访问有两种方法,一种是之前我们用RestTemplate的微服务名字来访问(Ribbon),一种就是我们的接口与注解(Feign)。
Feign能干什么?
我们主要用Feign来干什么呢?
- Feign旨在使编写Java Http客户端变得更容易
- 我们没学Feign之前是用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它(类似于以前Mapper接口上标注Mapper注解,现在是在一个微服务接口上面标注一个Feign注解即可。)即可完成对服务提供方的接口绑定,简化了使用SpringCloud Ribbon时,自动封装服务调用客户端的并发量。
Feign的核心就是动态代理,这是Feign的关键机制。
- 首先,如果你对某个接口定义了
@FeignClient
注解,Feign 就会针对这个接口创建一个动态代理。 - 接着你要是调用那个接口,本质就是会调用 Feign 创建的动态代理,这是核心中的核心。
- Feign的动态代理会根据你在接口上的
@RequestMapping
等注解,来动态构造出你要请求的服务的地址。 - 最后针对这个地址,发起请求、解析响应。
配置使用Feign
这里我继续以我之前SpringCloud的项目为例,来演示。
为了区别之前使用RestTemplate的消费者模块,这里我们再新建一个Feign的消费者模块。很多内容都不变,只需要改几个点
我们导入Feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
同时我们创建一个接口,用来封装调用注册的微服务。需要注意的是声明方法的注解、方法签名要和提供服务的方法完全一致
我们@FeignClient
后面的name是我们微服务模块注册的服务名字。
然后在我们的服务消费模块这里的Controller层这里,我们将Feign的service注入实例,就可以调用了。
@RestController
public class UserControllerConsumer {
@Autowired
private UserFeignService userFeignService;
@RequestMapping("/consumer/user/get/{id}")
public User get(@PathVariable int id){
return userFeignService.queryUserById(id);
}
@RequestMapping("/consumer/user/list")
public List<User> queryList(){
return userFeignService.queryListUser();
}
@RequestMapping("/consumer/user/add")
public boolean addUser(User user){
return userFeignService.addUser(user);
}
}
最后还需要在服务消费模块这里的主启动类开启我们的Feign,并且扫描我们另一个模块下的Feign服务。
@EnableEurekaClient
@EnableFeignClients("com.lin.springcloud")
@SpringBootApplication
public class Consumer_feign {
public static void main(String[] args) {
SpringApplication.run(Consumer_feign.class,args);
}
}
需要注意的是,上面我因为这样写,报错了,找了近二十分钟的bug。
@GetMapping("/user/get/{id}")
public User queryUserById(@PathVariable int id);
如果没有Feign的话,这样写是可以的,但是在Feign里面的服务接口里面是不可以的,要改成这样
@GetMapping("/user/get/{id}")
public User queryUserById(@PathVariable(value = "id") int id);
感谢参考解决bugFeign PathVariable annotation was empty on param 0.
然后我们依次启动服务器,是可以取出数据的。
Feign 是怎么和 Ribbon、Eureka 整合的?
首先,用户调用 Feign 创建的动态代理。
-
然后,Feign 调用 Ribbon 发起调用流程。
首先,Ribbon 会从 Eureka Client 里获取到对应的服务列表。
然后,Ribbon 使用负载均衡算法获得使用的服务。
最后,Ribbon 调用 Feign ,而 Feign 调用 HTTP 库最终调用使用的服务。
因为 Feign 和 Ribbon 都存在使用 HTTP 库调用指定的服务,那么两者在集成之后,必然是只能保留一个。比较正常的理解,也是保留 Feign 的调用,而 Ribbon 更纯粹的只负责负载均衡的功能。