11.SpringCloud Feign Basic

一、简介

在之前,我们学习了RibbonHystrix。微服务的负载均衡器以及服务熔断措施,大大稳定了微服务应用。

在使用Spring Cloud Ribbon的时候,是配合RestTemplate@LoadBalanced使用的,RestTemplate封装了http请求,提供了一套模板化调用方法。这时我们会发现,在实际开发中,对于更多的应用以及接口的调用,要写大量的RestTemplate模板化内容。

这时SpringCloud为了方便开发,实现了Feign这一组件,它简化了Ribbon,我们只需要创建接口并使用注解配置,即可完成对服务提供方的接口绑定。它具备可插拔的注解支持,包括Feign注解、JAX-RS注解。它也支持可插拔的编码器和解码器。Spring Cloud Feign还扩展了对Spring MVC注解的支持,同时还整合了Hystrix来提供服务熔断。

二、Spring Cloud Feign 入门

接着,我们从头开始使用Feign来充当服务消费方的角色。

1.初始化项目

首先我们创建一个Maven Project,springcloud-study-Feign

然后创建三个Module,分别是

  • eureka-server:注册中心
  • server-provider:服务提供方
  • server-consumer:服务消费方

1576130692600

2.添加依赖

这里为了方便,我们将所有依赖都加在父类的pom文件里,因为eurekaribbon依赖之前都讲过,这里只展示出Feign的依赖。

1
2
3
4
5
<!-- 依赖 Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

3.配置Eureka 注册中心

创建一个启动类,添加Eureka Server注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}

配置application.properties

1
2
3
4
5
6
7
8
spring.application.name=eureka-server
server.port=10001

# 取消向注册中心注册
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
# 取消多节点启动
eureka.instance.hostname=localhost

4.配置服务提供方

创建一个启动类,添加Eureka Client注解

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}

配置application.properties

1
2
3
4
5
spring.application.name=server-provider
server.port=9090

# 向注册中心注册
eureka.client.service-url.defaultZone=http://127.0.0.1:10001/eureka

编写Controller

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class BookController {

@Value("${server.port}")
private Integer port;

@GetMapping("/book/get/name")
public String get(@RequestParam("name") String name){
return "name : "+ name + " port : " + port;
}
}

5.配置服务消费方(Feign)

这时我们的重头戏就来了,首先是老套路,编写主程序类和配置。

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
1
2
3
4
5
6
7
spring.application.name=server-consumer
server.port=8080

# 向注册中心注册
eureka.client.service-url.defaultZone=http://127.0.0.1:10001/eureka
# 服务提供方名称
provider.service.name=server-provider

配置完这两项之后,我们需要写一个Sevice接口,用来消费服务,注意,这里的接口,就相当于之前使用Ribbon时编写的RestTemplate发送请求

1
2
3
4
5
6
7
@FeignClient(name = "${provider.service.name}")
public interface BookFeignService {

@GetMapping("/book/get/name")
String get(@RequestParam("name") String name);

}

使用Feign实现服务消费接口

  • 创建一个接口
  • 添加@FeignClient注解,具有一些属性
    • name:服务提供方的应用名称,这里我动态的配置
    • fallback:使用服务熔断后,可以配置这个,来进行服务熔断后的请求类,我们之后再细说
  • 使用SpringMVC注解,配置提供方的请求路径,并且入参和返回值要相同

编写Controller,注入Service接口,并调用服务。

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class BookController {

@Autowired
private BookFeignService bookFeignService;

@GetMapping("/book/get/name")
public String get(@RequestParam("name") String name){
return bookFeignService.get(name);
}
}

6.测试

这里先开启eureka-server

然后开启两个服务提供方,90909091

最后开启服务消费方

1576133292360

正确启动后访问我们的消费方接口

1576133311623)1576133320255

可以看到,正确负载均衡访问服务提供方,证明我们的Feign使用成功。

三、Spring Cloud Feign 改造

在上面的例子中,我们写了至少三次的api方法,即,服务提供方一次,服务调用方一次,Feign接口一次,如果接口越来越多,大量的复制粘贴是不可取的,这时,我们就需要改造一下我们的工程。

将接口抽象出来,以供提供方与消费方引用实现。

1.新建api工程

新建一个api工程,将所有的service-api类存放在这里,以便公共调用实现

1576137588973

创建BookService接口

1
2
3
4
5
6
public interface BookService {

@GetMapping("/book/get/name")
String get(@RequestParam("name") String name);

}

2.重构服务提供方

首先需要在pom中添加api工程的引用

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>com.enbuys</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>server-api</artifactId>
</dependency>
</dependencies>

其次就是修改Controller

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping("/book")
@RestController
public class BookController implements BookService {

@Value("${server.port}")
private Integer port;

@Override // /book/get/name
public String get(@RequestParam("name") String name){
return "name : "+ name + " port : " + port;
}
}

在之前的基础上,添加一个BookService的实现即可,这样我们只需重新接口中的方法即可,不用大量的复制粘贴。

==注意,这里就不用再写一遍@GetMapping了==,因为接口的映射会自动带过来

3.重构服务消费方

也需要先添加api工程引用

然后重构BookFeignService接口

1
2
3
@FeignClient(name = "${provider.service.name}")
public interface BookFeginService extends BookService {
}

这里只需集成BookService接口即可,并删除其中之前的方法

然后重构Controller

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/book")
public class BookController implements BookService {

@Autowired
private BookService bookService;

@Override // /book/get/name
public String get(@RequestParam("name") String name){
return bookService.get(name);
}
}

对于Controller来说也非常简单,实现BookService接口即可,会把@GetMapping注解带过来,==就不用再写一遍@GetMapping了==,因为接口的映射会自动带过来

此时,我们的Feign工程就改造完毕了,利用继承的特性,更好的实现接口定义与依赖共享

四、Spring Cloud Feign 与 Hystrix

之前说过Feign是将RibbonHystrix整合起来,让用户更方便的实现负载均衡和服务熔断,通过刚刚的代码,可以看到FeignRibbon客户端有很大区别,HystrixCommand被封装了起来,那么便不能使用@HystrixCommand注解方式来使用熔断,它提供了一种更好的方式来进行熔断。

我们通过改造我们上面的代码,来进行实现

开启熔断配置

首先要在配置文件中开启配置,因为在SpringCloud Dalston版本后,FeignHystrix默认是关闭的

server-consumer/application.properties

1
2
# 开启Fegin的Hystrix熔断
feign.hystrix.enabled=true

新建FallBack类实现

1
2
3
4
5
6
7
@Component
public class BookServiceFallBack implements BookFeginService {
@Override
public String get(String name) {
return "error fallback";
}
}

这里实现我们刚刚重构过会的BookFeignService接口,这样就可以直接重写方法了,而不是复制粘贴一遍。

==注意要开启@Component注解,加载到容器中==

配置FeignClient

最后一步,需要在刚刚的@FeignClient中添加fallback属性

1
2
3
@FeignClient(name = "${provider.service.name}",fallback = BookServiceFallBack.class)
public interface BookFeginService extends BookService {
}

这里使用了fallback属性。

还有一种方式是使用fallbackFactory,即工厂,那么实现了就需要继承自FallBackFactory类,并在泛型上使用BookFeginService接口。

测试

这里我们想测试熔断,只需将服务提供方关闭即可

1576154577788

可以看到,正确执行我们设置熔断后fallback方法