前面我们分别通过 SpringCloud-Ribbon
和 SpringCloud-Hystrix
实现了客户端负载均衡和服务容错,而 SpringCloud-Feign
不但整合了这两者的功能,而且还提供了一种比 Ribbon
更简单的服务调用方式那就是 声明式服务调用
。在 SpringCloud-Feign
中编写服务调用代码非常简单,几乎可以直接将服务提供者的代码复制过来,改为接口即可,下面通过例子来演示这个特性
搭建Feign Consumer
创建一个新的 SpringBoot
应用,版本号为 1.5.13.RELEASE
,artifactId
改为 Feign-Consumer
,并修改 pom.xml
引入下面这些依赖:
<artifactId>Feign-Consumer</artifactId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
除了 spring-cloud-starter-feign
的依赖以外,我们还引入了 spring-cloud-starter-eureka
目的是为了从 Eureka
服务注册中心中获取服务。
在 SpringBoot
的入口类中加入 @EnableFeignClients
和 @EnableDiscoveryClient
注解,用于开启 SpringCloud-Feign
和服务注册与发现:
/**
* @author BNTang
**/
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
在前面几节中,我曾在服务提供者 Eureka-Client
中定义了一个 UserController
,代码如下所示:
如果没有搭建的,前面几节中的文章如下
/**
* @author BNTang
**/
("user")
public class UserController {
private Logger log = LoggerFactory.getLogger(this.getClass());
("/{id:\\d+}")
public User get( Long id) {
log.info("获取用户id为 " + id + "的信息");
return new User(id, "bntang", "123456");
}
public List<User> get() {
List<User> list = new ArrayList<>();
list.add(new User(1L, "bntang", "123456"));
list.add(new User(2L, "scott", "123456"));
log.info("获取用户信息 " + list);
return list;
}
public void add( User user) {
log.info("新增用户成功 " + user);
}
public void update( User user) {
log.info("更新用户成功 " + user);
}
("/{id:\\d+}")
public void delete( Long id) {
log.info("删除用户成功 " + id);
}
}
在 SpringCloud-Ribbon
中访问这些服务需要通过 RestTemplate
对象来实现,并且参数绑定的过程也比较繁琐。SpringCloud-Feign
对这个步骤进行了进一步的封装,在 Feign Consumer
中调用这些服务只需要定义一个 UserService
接口,然后将 UserController
中的代码复制到 UserService
接口,然后将方法体去掉即可,如下所示:
/**
* @author BNTang
**/
("Server-Provider")
public interface UserService {
("user/{id}")
public User get( ("id") Long id);
("user")
public List<User> get();
("user")
public void add( User user);
("user")
public void update( User user);
("user/{id}")
public void delete( ("id") Long id);
}
对比 Feign Consumer
中的 UserService
和 Eureka-Client
中的 UserController
代码,两者是不是很相似?
在 UserService
中,我们通过 @FeignClient("Server-Provider")
注解来获取我们需要的服务,其中 Server-Provider
不区分大小写。需要注意的是,在定义各参数绑定时,@RequestParam
、@RequestHeader
等可以指定参数名称的注解,它们的 value
千万不能少。在 SpringMVC 程序中,这些注解会根据参数名来作为默认值,但是在 Feign 中绑定参数必须通过 value
属性来指明具体的参数名,不然会抛出 illegalStateException
异常,value
属性不能为空。
接下来我们在 Feign Consumer
中定义一个 TestController
,来调用 UserService
中定义的服务,代码如下所示:
/**
* @author BNTang
**/
public class TestController {
private UserService userService;
("user/{id}")
public User getUser( Long id) {
return userService.get(id);
}
("user")
public List<User> getUsers() {
return userService.get();
}
("user")
public void addUser() {
User user = new User(1L, "bntang", "123456");
userService.add(user);
}
("user")
public void updateUser() {
User user = new User(1L, "bntang", "123456");
userService.update(user);
}
("user/{id}")
public void deleteUser( Long id) {
userService.delete(id);
}
}
最后在配置一下 application.yml
,配置内容如下所示:
server
port9000
spring
application
name Server-Consumer
eureka
client
serviceUrl
defaultZone http //bntang 123456@peer1 8080/eureka/,http //bntang 123456@peer2 8081/eureka/