SpringCloud微服务知识整理六:声明式服务调用 Spring Cloud Feign

释放双眼,带上耳机,听听看~!

什么是Spring Cloud Feign

Spring Cloud Feign 是基于 Netflix Feign 实现的,整合了 Spring Cloud Ribbon 和 Spring Cloud Hystrix,除了提供这两者的强大功能之外,还提供了一种声明式的 Web 服务客户端定义方式

一、快速入门

1、创建一个 Spring Boot 基础工程,取名为 feign-consumer,并在 pom.xml 中引入 spring-cloud-starter-eureka 和 spring-cloud-starter-feign 依赖。


1
2
3
4
5
6
1<dependency>
2          <groupId>org.springframework.cloud</groupId>
3          <artifactId>spring-cloud-starter-feign</artifactId>
4</dependency>
5
6

2、在主类上通过 @EnableFeignClients 注解开启 Spring Cloud Feign 的支持功能。


1
2
3
4
5
6
7
8
9
10
1@SpringBootApplication
2@EnableDiscoveryClient
3@EnableFeignClients
4public class FeignConsumerApplication {
5    public static void main(String[] args) {
6        SpringApplication.run(FeignConsumerApplication.class, args);
7    }
8}
9
10

3、定义 HelloService 接口,通过 @FeignClient 注解指定服务名来绑定服务,然后再使用 Spring MVC 的注解来绑定具体该服务提供的 REST 接口。


1
2
3
4
5
6
7
1@FeignClient(value = "hello-service")
2public interface HelloService {
3    @RequestMapping(value = "/hello")
4    String hello();
5}
6
7

4、创建一个 ConsumerController 来实现对 Feign 客户端的调用。使用 @Autowired 直接注入上面定义的 HelloService 实例,并在 helloConsumer 函数中调用这个绑定了 hello-service 服务接口的客户端来向该服务发起 /hello接口的调用。


1
2
3
4
5
6
7
8
9
10
11
12
13
1@RestController
2public class ConsumerController {
3
4    @Autowired
5    HelloService helloService;
6
7    @RequestMapping(value = "feign-consumer", method = RequestMethod.GET)
8    public String helloConsumer(){
9        return helloService.hello();
10    }
11}
12
13

5、在 application.properties 中指定注册中心,并定义自身的服务名为 feign-consumer


1
2
3
4
5
6
1spring.application.name=feign-consumer
2server.port=9001
3
4eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
5
6

6、验证
启动 eureka-server 和 两个hello-service,然后启动 feign-consumer,发送请求到 http://localhost:9001/feign-consumer,正确返回。
与 Ribbon 不同的是,通过 Feign 我们只需定义服务绑定接口,以声明式的方法,优雅而简单地实现了服务调用。

二、参数绑定

传入参数和反回复杂对象的用法:
1、先扩展一下服务提供方 hello-service 。增加下面这些接口定义。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
2    public String hello1(@RequestParam String name){
3        return "HELLO " + name;
4    }
5
6    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
7    public User hello2(@RequestHeader String name, @RequestHeader Integer age){
8        return new User(name, age);
9    }
10
11    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
12    public String hello3(@RequestBody User user){
13        return "HELLO," + user.getName()+","+user.getAge();
14    }
15
16

2、定义User 对象,需要注意,这里必须要有User 的默认构造函数。不然,Spring Cloud Feign 根据 JSON 字符串转换 User 对象会抛出异常。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1public class User {
2    private String name;
3
4    private Integer age;
5
6    public User() {
7    }
8
9    public User(String name, Integer age) {
10        this.name = name;
11        this.age = age;
12    }
13
14    public String getName() {
15        return name;
16    }
17
18    public void setName(String name) {
19        this.name = name;
20    }
21
22    public Integer getAge() {
23        return age;
24    }
25
26    public void setAge(Integer age) {
27        this.age = age;
28    }
29
30    @Override
31    public String toString() {
32        return "User{" +
33                "name='" + name + '\'' +
34                ", age=" + age +
35                '}';
36    }
37}
38
39

3、在 feign-consumer 中创建与上面一样的 User 类。
4、在 HelloService 接口中增加对上述三个新增接口的绑定声明。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1@FeignClient(value = "hello-service")
2public interface HelloService {
3
4    @RequestMapping(value = "/index")
5    String hello();
6
7    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
8    String hello1(@RequestParam(value = "name") String name);
9
10    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
11    User hello2(@RequestParam(value = "name") String name, @RequestHeader(value = "age") Integer age);
12
13    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
14    String hello3(@RequestBody User user);
15}
16
17

5、在 ConsumerController 中新增一个 /feign-consumer2 接口


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1@RestController
2public class ConsumerController {
3
4    @Autowired
5    HelloService helloService;
6
7    @RequestMapping(value = "feign-consumer", method = RequestMethod.GET)
8    public String helloConsumer(){
9        return helloService.hello();
10    }
11
12    @RequestMapping(value = "/feign-consumer2", method = RequestMethod.GET)
13    public String helloConsumer2(){
14        StringBuilder sb = new StringBuilder();
15        sb.append(helloService.hello1("didi")).append("\n");
16        sb.append(helloService.hello2("didi", 18)).append("\n");
17        sb.append(helloService.hello3(new User("didi", 20))).append("\n");
18        return sb.toString();
19    }
20}
21
22

6、验证
http://localhost:9001/feign-consumer2 ,触发 HelloService 对新增接口的调用

三、继承特性

通过 Spring Cloud Feign 的继承特性来实现 REST 接口定义的复用
1、为了能够复用 DTO 与接口定义,我们先创建一个基础的 Maven 工程,命名为 hello-service-api。
2、在 hello-service-api 中需要定义可同时复用于服务端与客户端的接口,我们要使用到 Spring MVC 的注解,所以在 pom.xml 中引入 spring-boot-starter-web 依赖
3、将 User 对象复制到 hello-service-api 工程中。
4、在 hello-service-api 工程中创建 HelloService 接口


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1@RequestMapping(value = "/refactor")
2public interface HelloService {
3
4    @RequestMapping(value = "/hello4", method = RequestMethod.GET)
5    String hello4(@RequestParam(value = "name") String name);
6
7    @RequestMapping(value = "/hello5", method = RequestMethod.GET)
8    User hello5(@RequestHeader(value = "name") String name, @RequestHeader(value = "age") Integer age);
9
10    @RequestMapping(value = "/hello6", method = RequestMethod.POST)
11    String hello6(@RequestBody User user);
12}
13
14

5、执行命令 mvn install 将该模块构建到本地仓库。
6、对 hello-service 进行重构,在 pom.xml 的 dependency 节点中,新增对 hello-service-api 的依赖。


1
2
3
4
5
6
7
1<dependency>
2    <groupId>com.didispace</groupId>
3    <artifactId>hello-service-api</artifactId>
4    <version>0.0.1-SNAPSHOT</version>
5</dependency>
6
7

7、创建 RefactorHelloController 类实现 hello-service-api 中定义的 HelloService 接口,并参考之前的 HelloController 来实现这三个接口


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1@RestController
2public class RefactorHelloController implements HelloService {
3
4    @Override
5    public String hello4(@RequestParam(value = "name") String name) {
6        return "HELLO " + name;
7    }
8
9    @Override
10    public User hello5(@RequestHeader(value = "name") String name, @RequestHeader(value = "age") Integer age) {
11        return new User(name, age);
12    }
13
14    @Override
15    public String hello6(@RequestBody User user) {
16        return "HELLO," + user.getName()+","+user.getAge();
17    }
18}
19
20

8、完成了服务提供者的重构,接下来在服务消费者 feign-consumer 的 pom.xml 文件中,如在服务提供者一样,新增对 hello-service-api 的依赖。
9、创建 RefactorHelloService 接口,并继承 hello-service-api 包中的 HelloService 接口,然后添加 @FeignClient 注解来绑定服务。


1
2
3
4
5
1@FeignClient (value="Hello-Service")
2public interface RefactorHelloService extends HelloService{
3}
4
5

10、在ConsumerController中注入RefactorHelloService 并新增一个请求来触发


1
2
3
4
5
6
7
8
9
10
1@RequestMapping(value = "/feign-consumer3",method=RequestMethod.GET)
2    public String helloConsumer3(){
3        StringBuilder sb = new StringBuilder();
4        sb.append(refactorHelloService.hello("MIMI")).append("\n");
5        sb.append(refactorHelloService.hello("MIMI","123456")).append("\n");
6        sb.append(refactorHelloService.hello(new com.didispace.dto.User("MIMI","123456"))).append("\n");
7        return sb.toString();
8    }
9
10

四、Ribbon配置

1、全局配置


1
2
3
4
1ribbon.ConnectTimeout=500
2ribbon.ReadTimeout=5000
3
4

2、指定服务配置


1
2
3
4
5
6
7
1HELLO-SERVICE.ribbon.ConnectTimeout=500
2HELLO-SERVICE.ribbon.ReadTimeout=2000
3HELLO-SERVICE.ribbon.okToRetryOnAllOperations=true
4HELLO-SERVICE.ribbon.MaxAutoRetriesNextServer=2
5HELLO-SERVICE.ribbon.MaxAutoRetries=1
6
7

3、重试机制
默认实现

五、Hystrix配置

1、全局配置


1
2
3
4
5
6
7
1hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
2#关闭 Hystrix 功能
3#feign.hystrix.enabled=false
4#关闭熔断功能
5#hystrix.command.default.execution.timeout.enabled=false
6
7

2、禁用Hystrix


1
2
3
1feign.hystrix.enabled=false
2
3

针对某个客户端关闭 Hystrix ,通过使用 @Scope(“prototype”) 注解为指定的客户端配置 Feign.Builder 实例
构建一个关闭 Hystrix 的配置类


1
2
3
4
5
6
7
8
9
10
11
12
1@Configuration
2public class DisableHystrixConfiguration {
3
4    @Bean
5    @Scope("prototype")
6    public Feign.Builder feignBuilder(){
7        return Feign.builder();
8    }
9
10}
11
12

在 HelloService 的 @FeignClient 注解中,通过 configuration 参数引入上面实例的配置


1
2
3
4
5
6
7
1@FeignClient(value = "SERIVCE-USER",configuration = DisableHystrixConfiguration.class)
2@Service
3public interface HelloService {
4   ···
5}
6
7

4、指定命令配置
对 /hello 接口的熔断时间的配置


1
2
3
1hystrix.command.hello.execution.isolation.thread.timeoutInMilliseconds=5000
2
3

5、服务降级配置
对 feign-consumer 工程进行改造,为HelloService接口实现一个服务降级类


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1@Component
2public class HelloServiceFallBack implements HelloService {
3    @Override
4    public String hello() {
5        return "error";
6    }
7
8    @Override
9    public String hello(@RequestParam("name") String name) {
10        return "error";
11    }
12
13    @Override
14    public User hello(@RequestHeader("name") String name,@RequestHeader("password") String password) {
15        return new User("未知","0");
16    }
17
18    @Override
19    public String hello(User user) {
20        return "error";
21    }
22}
23
24

在服务绑定接口 HelloService 中,通过 @FeignClient 注解的 fallback 属性来指定对应的服务降级实现类


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1@FeignClient(value = "SERIVCE-USER",fallback = HelloServiceFallBack.class)
2@Service
3public interface HelloService {
4
5    @RequestMapping("/hello")
6    String hello();
7
8    @GetMapping(value = "/hello1")
9    String hello(@RequestParam("name") String name);
10
11    @GetMapping(value = "/hello2")
12    User hello(@RequestHeader("name") String name,@RequestHeader("password") String password);
13
14    @PostMapping(value = "/hello3")
15    String hello(@RequestBody User user);
16}
17
18

六、其他配置

1、请求压缩


1
2
3
4
5
6
7
1feign.compression.request.enabled=true
2feign.compression.response.enabled=true
3#设置压缩的大小下限,超过的才进行压缩,以下配置为默认值
4feign.compression.request.mime-types=text/xml,application/xml,application/json
5feign.compression.request.min-request-size=2048
6
7

2、日志配置


1
2
3
1logging.level.com.didispace.web.HelloService=DEBUG
2
3

feign-consumer 启动类配置


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1@SpringBootApplication
2@EnableFeignClients
3@EnableDiscoveryClient
4public class SpringcloudFeignApplication {
5
6    @Bean
7    Logger.Level feignLoggerLevel(){
8        return Logger.Level.FULL;
9    }
10
11    public static void main(String[] args) {
12        SpringApplication.run(SpringcloudFeignApplication.class, args);
13    }
14}
15
16

也可以通过实现配置类,然后在具体的Feign 客户端来指定配置类以实现是否要调整不同的日志界别


1
2
3
4
5
6
7
8
9
10
11
1@Configuration
2public class FullLogConfiguration {
3
4    @Bean
5    Logger.Level feignLoggerLevel(){
6        return Logger.Level.FULL;
7    }
8
9}
10
11

1
2
3
4
5
6
1@FeignClient(name="HELLO_SERVICE",configuration = FullLogConfiguration.class
2public interface HelloService{
3   ...
4}
5
6

Feign的Logger级别:
NONE
BASIC
HEADERS
FULL

给TA打赏
共{{data.count}}人
人已打赏
安全经验

Google Adsense老手经验

2021-10-11 16:36:11

安全经验

安全咨询服务

2022-1-12 14:11:49

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索