在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client, Spring的RestTemplate。但是,用起来最方便、最优雅的还是要属Feign了。
Feign简介
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。比如:
@RestController
public class DeptController_Consumer {
@Autowired
DeptClientService clientService; // 远程服务
@RequestMapping(value = "/consumer/dept/add")
public boolean add(Dept dept) {
return clientService.add(dept); //通过HTTP调用远程add服务接口
}
@RequestMapping(value = "/consumer/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id) {
return clientService.get(id); //通过HTTP调用远程get服务接口
}
@RequestMapping(value = "/consumer/dept/list", method = RequestMethod.GET)
public List<Dept> list() {
return clientService.list(); //通过HTTP调用远程list服务接口
}
}
以上就是使用feign进行远程方法调用,很像我们的controller调用service接口
开发者通过clientService.add(dept)…就能完成发送HTTP请求和解码HTTP返回结果并封装成对象的过程。
Feign的定义
为了让Feign知道在调用方法时应该向哪个地址发请求以及请求需要带哪些参数,我们需要定义一个接口:
@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService {
// 以下接口其实就是拿MICROSERVICECLOUD-DEPT服务提供者提供的接口(controller层提供的接口)
// 我们将这些接口统一的放在一个自定义接口DeptClientService当中,
// 接口上添加@FeignClient(value = "服务提供者微服务名称"),我们就可以做到使用HTTP请求远程服务时能与
// 调用本地方法一样的编码体验(此自定义的接口里的所有方法,其实就是服务提供者远程提供的接口方法,
// 我们要调用服务提供者提供的方法也就是接口,我们只需要将此DeptClientService接口对象 @Autowired到
// 要调用的位置,类似于传统的controller层调用service层)
// 开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求,
// 这就是面向接口开发的魅力,就问你牛B吗?
@RequestMapping(value = "/dept/add",method = RequestMethod.POST)
public boolean add(@RequestBody Dept dept);
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") Long id);
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list();
}
接口其实就是拿MICROSERVICECLOUD-DEPT服务提供者提供的接口(controller层提供的接口)
我们将这些接口统一的放在一个自定义接口类DeptClientService当中(一般我们将自定义接口类放在统一的api工程当中,方便复用),
接口上添加@FeignClient(value = "服务提供者微服务名称"),我们就可以做到使用HTTP请求远程服务时能与
调用本地方法一样的编码体验(此自定义的接口里的所有方法,其实就是服务提供者远程提供的接口方法,
我们要调用服务提供者提供的方法也就是接口,我们只需要将此DeptClientService接口对象 @Autowired到
要调用的位置,类似于传统的controller层调用service层)
开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求,
这就是面向接口开发的魅力,就问你牛B吗?
@FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入。
@RequestMapping表示在调用该方法时需要向/…发送GET/POST/…请求。
@PathVariable与SpringMVC中对应注解含义相同。
以下就是feign使用案例,pom文件依赖就不展示了:
Feign原理简述
1 Spring Cloud应用在启动时,程序会进行包扫描,扫描所有包下所有标有@FeignClient注解的接口,生成代理对象,并将这些接口注入到spring的IOC容器中
2 当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate代理对象。
该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里
3 RequestTemplate再生成Request。
4 然后将Request交给client处理,这个client默认是JDK的HttpUrlConnection(也可以是HttpClient或Okhttp,需要配置)
5 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。
在本例中,我们将Feign与Eureka和Ribbon组合使用,@FeignClient(value = “MICROSERVICECLOUD-DEPT”)意为通知Feign在调用该接口方法时要向Eureka中查询名为MICROSERVICECLOUD-DEPT的服务,从而得到服务URL。
总结
通过Feign, 我们能把HTTP远程调用对开发者完全透明,得到与调用本地方法一致的编码体验。这一点与阿里Dubbo中暴露远程服务的方式类似,区别在于Dubbo是基于私有二进制协议,而Feign本质上还是个HTTP客户端。如果是在用Spring Cloud Netflix搭建微服务,那么Feign无疑是最佳选择。