简介
之前学习了Ribbon,Ribbon结合RestTemplate可以实现服务远端调用和负载均衡的效果。而Feign也可以用来做远端调用以及负载均衡,那么一山不容二虎,为什么有了Ribbon又要出来Feign呢?
官网的解释如下:
Feign 是一个声明式 Web 服务客户端。它使编写 Web 服务客户端变得更容易。要使用 Feign 创建一个接口并对其进行注释。它具有可插入的注释支持,包括 Feign 注释和 JAX-RS 注释。 Feign 还支持可插拔的编码器和解码器。 Spring Cloud 添加了对 Spring MVC 注释的支持,并支持使用 Spring Web 中默认使用的相同 HttpMessageConverters。 Spring Cloud 集成了 Eureka、Spring Cloud CircuitBreaker 和 Spring Cloud LoadBalancer,在使用 Feign 时提供负载均衡的 http 客户端。
说人话就是:Feign是一个声明式的Web服务(Service层)客户端,使得编写Web服务客户端变得非常容易,只需要创建一个接口,然后在上面添加注解即可。
之前使用 Ribbon,使得我们可以面向微服务编程,在restTemplate的路径上,可以通过微服务名进行访问。但是现在我们更习惯于面向接口编程,接口+注解的方式,即可更加优雅的调用我们的微服务,而Feign可以做到。
使用
Feign的搭建
新建一个模块,专门用来做 Feign 的实现。
pom.xml 如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudservice</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloudservice-feign</artifactId>
<dependencies>
<!-- 引入自定义的 api -->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloudservice-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 引入 feign 依赖,feign 集成了 ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
</project>
接下来创建一个接口。这里要注意:
- 用Feign来实现微服务之间接口的调用,就是直接将被调用的微服务中的方法和参数直接复制过来。
- 方法上的mapping映射路径要写完整,比如controller上也加了RequestMapping,则复制过来的路径要在方法的路径上加上controller上的路径。
- 如果方法上的mapping映射路径中有pathVariable参数,则必须用@PathVariable指明,不可省略value属性,不然可能会有问题。
现在我要实现,feign去调用服务提供者。那么我就将服务提供者的方法全部复制到接口中。并且我们要在接口上标注 @FeignClient
注解,并指定value属性,也即微服务名,大写小写都可以,找到对应的微服务。
package com.zxb.springcloud.service;
import com.zxb.springcloud.bean.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
// 微服务名字
@FeignClient(value = "SPRINGCLOUDSERVICE-USER")
public interface UserClientService {
@PostMapping(path = "/user/add")
public Boolean addUser(@RequestBody User user);
@GetMapping(path = "/user/del/{id}")
public Integer deleteUser(@PathVariable(value = "id") Integer id);
@PutMapping(path = "/user/update")
public void updateUser(@RequestBody User user);
@GetMapping(path = "/user/get/{id}")
public User getUserById(@PathVariable(value = "id") Integer id);
@GetMapping(path = "/user/get")
public List<User> getUsers();
}
feign就配置好了。随后将此模块,maven clean,maven install ,供其它模块使用。如果出现install失败,则先install父工程,再去install此模块即可。
消费者的搭建
之前使用Ribbon+RestTemplate搭建消费者。而现在,使用声明式的 Feign 来进行搭建。
创建一个新的模块。
pom.xml 如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudservice</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloudservice-consumer-user-feign-80</artifactId>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloudservice-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloudservice-feign</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
</project>
application.properties 配置文件如下:
server.port=80
eureka.client.register-with-eureka=false
eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
此时我们再去写 controller
package com.zxb.springcloud.controller;
import com.zxb.springcloud.bean.User;
import com.zxb.springcloud.service.UserClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class UserController_Consumer {
// Ribbon 的方式
// private static final String REST_URL = "http://SPRINGCLOUDSERVICE-USER";
//
// @Autowired
// private RestTemplate restTemplate;
// Feign 的方式
@Autowired
private UserClientService userClientService;
@GetMapping(path = "/feign/user/get/{id}")
public User getById(@PathVariable(value = "id") Integer id) {
return userClientService.getUserById(id);
}
@PostMapping(path = "/feign/user/add")
public boolean addUser(User user) {
return userClientService.addUser(user);
}
@GetMapping(path = "/feign/user/get")
public List<User> getAll() {
return userClientService.getUsers();
}
@DeleteMapping(path = "/feign/user/del/{id}")
public Integer deleteUserById(@PathVariable(value = "id") Integer id) {
return userClientService.deleteUser(id);
}
}
可以看到,Feign的方式更加符合我们现在的编程方式,controller去调用service。在消费者的controller中,其实就是去调用了feign中的方法,由feign根据注解上的微服务名称去实现远端调用对应的provider的方法,然后找到对应的映射地址,进行调用。
测试
调用消费者的接口,http://localhost/feign/user/get/ ,进行测试
通过 Feign,也是成功的实现了微服务直接接口的调用,并且 Feign 集成了 Ribbon,也自带负载均衡功能,多次访问默认采用轮询算法。