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 依赖。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
2、在主类上通过 @EnableFeignClients 注解开启 Spring Cloud Feign 的支持功能。
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignConsumerApplication { public static void main(String[] args) { SpringApplication.run(FeignConsumerApplication.class, args); } }
3、定义 HelloService 接口,通过 @FeignClient 注解指定服务名来绑定服务,然后再使用 Spring MVC 的注解来绑定具体该服务提供的 REST 接口。
@FeignClient(value = "hello-service") public interface HelloService { @RequestMapping(value = "/hello") String hello(); }
4、创建一个 ConsumerController 来实现对 Feign 客户端的调用。使用 @Autowired 直接注入上面定义的 HelloService 实例,并在 helloConsumer 函数中调用这个绑定了 hello-service 服务接口的客户端来向该服务发起 /hello接口的调用。
@RestController public class ConsumerController { @Autowired HelloService helloService; @RequestMapping(value = "feign-consumer", method = RequestMethod.GET) public String helloConsumer(){ return helloService.hello(); } }
5、在 application.properties 中指定注册中心,并定义自身的服务名为 feign-consumer
spring.application.name=feign-consumer server.port=9001 eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
6、验证
启动 eureka-server 和 两个hello-service,然后启动 feign-consumer,发送请求到 http://localhost:9001/feign-consumer,正确返回。
与 Ribbon 不同的是,通过 Feign 我们只需定义服务绑定接口,以声明式的方法,优雅而简单地实现了服务调用。
二、参数绑定
传入参数和反回复杂对象的用法:
1、先扩展一下服务提供方 hello-service 。增加下面这些接口定义。
@RequestMapping(value = "/hello1", method = RequestMethod.GET) public String hello1(@RequestParam String name){ return "HELLO " + name; } @RequestMapping(value = "/hello2", method = RequestMethod.GET) public User hello2(@RequestHeader String name, @RequestHeader Integer age){ return new User(name, age); } @RequestMapping(value = "/hello3", method = RequestMethod.POST) public String hello3(@RequestBody User user){ return "HELLO," + user.getName()+","+user.getAge(); }
2、定义User 对象,需要注意,这里必须要有User 的默认构造函数。不然,Spring Cloud Feign 根据 JSON 字符串转换 User 对象会抛出异常。
public class User { private String name; private Integer age; public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
3、在 feign-consumer 中创建与上面一样的 User 类。
4、在 HelloService 接口中增加对上述三个新增接口的绑定声明。
@FeignClient(value = "hello-service") public interface HelloService { @RequestMapping(value = "/index") String hello(); @RequestMapping(value = "/hello1", method = RequestMethod.GET) String hello1(@RequestParam(value = "name") String name); @RequestMapping(value = "/hello2", method = RequestMethod.GET) User hello2(@RequestParam(value = "name") String name, @RequestHeader(value = "age") Integer age); @RequestMapping(value = "/hello3", method = RequestMethod.POST) String hello3(@RequestBody User user); }
5、在 ConsumerController 中新增一个 /feign-consumer2 接口
@RestController public class ConsumerController { @Autowired HelloService helloService; @RequestMapping(value = "feign-consumer", method = RequestMethod.GET) public String helloConsumer(){ return helloService.hello(); } @RequestMapping(value = "/feign-consumer2", method = RequestMethod.GET) public String helloConsumer2(){ StringBuilder sb = new StringBuilder(); sb.append(helloService.hello1("didi")).append("\n"); sb.append(helloService.hello2("didi", 18)).append("\n"); sb.append(helloService.hello3(new User("didi", 20))).append("\n"); return sb.toString(); } }
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 接口
@RequestMapping(value = "/refactor") public interface HelloService { @RequestMapping(value = "/hello4", method = RequestMethod.GET) String hello4(@RequestParam(value = "name") String name); @RequestMapping(value = "/hello5", method = RequestMethod.GET) User hello5(@RequestHeader(value = "name") String name, @RequestHeader(value = "age") Integer age); @RequestMapping(value = "/hello6", method = RequestMethod.POST) String hello6(@RequestBody User user); }
5、执行命令 mvn install 将该模块构建到本地仓库。
6、对 hello-service 进行重构,在 pom.xml 的 dependency 节点中,新增对 hello-service-api 的依赖。
<dependency> <groupId>com.didispace</groupId> <artifactId>hello-service-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
7、创建 RefactorHelloController 类实现 hello-service-api 中定义的 HelloService 接口,并参考之前的 HelloController 来实现这三个接口
@RestController public class RefactorHelloController implements HelloService { @Override public String hello4(@RequestParam(value = "name") String name) { return "HELLO " + name; } @Override public User hello5(@RequestHeader(value = "name") String name, @RequestHeader(value = "age") Integer age) { return new User(name, age); } @Override public String hello6(@RequestBody User user) { return "HELLO," + user.getName()+","+user.getAge(); } }
8、完成了服务提供者的重构,接下来在服务消费者 feign-consumer 的 pom.xml 文件中,如在服务提供者一样,新增对 hello-service-api 的依赖。
9、创建 RefactorHelloService 接口,并继承 hello-service-api 包中的 HelloService 接口,然后添加 @FeignClient 注解来绑定服务。
@FeignClient (value="Hello-Service") public interface RefactorHelloService extends HelloService{ }
10、在ConsumerController中注入RefactorHelloService 并新增一个请求来触发
@RequestMapping(value = "/feign-consumer3",method=RequestMethod.GET) public String helloConsumer3(){ StringBuilder sb = new StringBuilder(); sb.append(refactorHelloService.hello("MIMI")).append("\n"); sb.append(refactorHelloService.hello("MIMI","123456")).append("\n"); sb.append(refactorHelloService.hello(new com.didispace.dto.User("MIMI","123456"))).append("\n"); return sb.toString(); }
四、Ribbon配置
1、全局配置
ribbon.ConnectTimeout=500 ribbon.ReadTimeout=5000
2、指定服务配置
HELLO-SERVICE.ribbon.ConnectTimeout=500 HELLO-SERVICE.ribbon.ReadTimeout=2000 HELLO-SERVICE.ribbon.okToRetryOnAllOperations=true HELLO-SERVICE.ribbon.MaxAutoRetriesNextServer=2 HELLO-SERVICE.ribbon.MaxAutoRetries=1
3、重试机制
默认实现
五、Hystrix配置
1、全局配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000 #关闭 Hystrix 功能 #feign.hystrix.enabled=false#关闭熔断功能 #hystrix.command.default.execution.timeout.enabled=false
2、禁用Hystrix
feign.hystrix.enabled=false
针对某个客户端关闭 Hystrix ,通过使用 @Scope(“prototype”) 注解为指定的客户端配置 Feign.Builder 实例
构建一个关闭 Hystrix 的配置类
@Configuration public class DisableHystrixConfiguration { @Bean @Scope("prototype") public Feign.Builder feignBuilder(){ return Feign.builder(); } }
在 HelloService 的 @FeignClient 注解中,通过 configuration 参数引入上面实例的配置
@FeignClient(value = "SERIVCE-USER",configuration = DisableHystrixConfiguration.class) @Service public interface HelloService { ··· }
4、指定命令配置
对 /hello 接口的熔断时间的配置
hystrix.command.hello.execution.isolation.thread.timeoutInMilliseconds=5000
5、服务降级配置
对 feign-consumer 工程进行改造,为HelloService接口实现一个服务降级类
@Component public class HelloServiceFallBack implements HelloService { @Override public String hello() { return "error"; } @Override public String hello(@RequestParam("name") String name) { return "error"; } @Override public User hello(@RequestHeader("name") String name,@RequestHeader("password") String password) { return new User("未知","0"); } @Override public String hello(User user) { return "error"; } }
在服务绑定接口 HelloService 中,通过 @FeignClient 注解的 fallback 属性来指定对应的服务降级实现类
@FeignClient(value = "SERIVCE-USER",fallback = HelloServiceFallBack.class) @Service public interface HelloService { @RequestMapping("/hello") String hello(); @GetMapping(value = "/hello1") String hello(@RequestParam("name") String name); @GetMapping(value = "/hello2") User hello(@RequestHeader("name") String name,@RequestHeader("password") String password); @PostMapping(value = "/hello3") String hello(@RequestBody User user); }
六、其他配置
1、请求压缩
feign.compression.request.enabled=true feign.compression.response.enabled=true #设置压缩的大小下限,超过的才进行压缩,以下配置为默认值 feign.compression.request.mime-types=text/xml,application/xml,application/json feign.compression.request.min-request-size=2048
2、日志配置
logging.level.com.didispace.web.HelloService=DEBUG
feign-consumer 启动类配置
@SpringBootApplication @EnableFeignClients @EnableDiscoveryClient public class SpringcloudFeignApplication { @Bean Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } public static void main(String[] args) { SpringApplication.run(SpringcloudFeignApplication.class, args); } }
也可以通过实现配置类,然后在具体的Feign 客户端来指定配置类以实现是否要调整不同的日志界别
@Configuration public class FullLogConfiguration { @ 20000 Bean Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } }
@FeignClient(name="HELLO_SERVICE",configuration = FullLogConfiguration.class public interface HelloService{ ... }
Feign的Logger级别:
NONE
BASIC
HEADERS
FULL
- 笔记:Spring Cloud Feign 声明式服务调用
- Spring Cloud Feign 声明式服务调用
- Spring Cloud中声明式服务调用Feign (11)
- SpringCloud之声明式服务调用Spring Cloud Feign实例
- 第九章 Spring Cloud Feign声明式调用服务
- 干货分享微服务spring-cloud(5.声明式服务调用feign)
- Spring Cloud中声明式服务调用Feign
- 【图文经典版】声明式调用服务SpringCloud之Feign实例讲解
- Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务
- Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务
- SpringCloud零基础上手(四)——服务发现以及Feign(声明式RESTful服务调用)
- spring cloud 系列第4篇 —— feign 声明式服务调用 (F版本)
- Spring Cloud Feign 声明式服务调用
- spring cloud Feign(声明式服务调用)
- 【Spring Cloud】--声明式服务调用Feign
- Spring Cloud 声明式服务调用 Feign
- Spring Cloud中声明式服务调用Feign
- 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务
- SpringCloud 通过Feign 调用其他服务下载文件
- 使用Spring cloud Feign在后台服务之间调用传递Multipart无法传递的问题