声明式服务调用SpringCloud Feign
声明式服务调用SpringCloud Feign
文章目录
- 声明式服务调用SpringCloud Feign
- 快速入门
- 参数绑定
- 继承特性
- Ribbon 配置
- 全局配置
- 指定服务配置
- 重试机制
- Hystrix 配置
- 全局配置
- 禁用 Hystrix
- 指定命令配置
- 服务降级配置
- 其他配置
- 请求压缩
- 日志配置
快速入门
创建项目 feign-consumer 9010
pom 依赖
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zk.springcloud.feign</groupId>
<artifactId>springcloud-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springcloud-feign</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建应用启动类,并通过 @EnableFeignClients 注解开启 Spring Cloud Feign 的支持功能
@SpringBootApplication @EnableFeignClients @EnableDiscoveryClient public class SpringcloudFeignApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudFeignApplication.class, args); } }
定义 HelloService 接口,通过 @FeignClient 注解指定服务名来绑定服务,然后再使用 SpringMVC 的注解来绑定具体该服务提供的 REST 接口
这里服务名不区分大小写,所以使用 SERIVCE-USER 和 service-user 都是可以的
@FeignClient("SERIVCE-USER") @Service public interface HelloService { @RequestMapping("/hello") String hello(); }
方法调用
@RestController public class ConsumerController { @Autowired HelloService helloService; @GetMapping(value = "/feign-consumer") public String helloConsumer(){ return helloService.hello(); } }
配置文件
spring.application.name=feign-consumer
server.port=9010
eureka.client.service-url.defaultZone=http://localhost:9001/eureka/,http://localhost:9004/eureka/
测试验证,如之前验证 Ribbon 客户端负载均衡一样,先启动注册中心以及两个 SERVICE-USER ,然后启动 FEIGN-CONSUMER ,多次调用 http://localhost:9010/feign-consumer , 结果与之前 Ribbon 实现时一样的效果 Hello World 9006 和 Hello World 9003 切换显示。依然是利用 Ribbon 维护对 SERVICE-USER 的服务列表信息,并且通过轮询实现了客户端负载均衡。
参数绑定
feign-consumer 9010 , HelloService 添加方法
@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);
User 对象如下, 这里必须要有 User 的默认构造函数,不然 Spring Cloud Feign 根据 JSON 字符串转换 User 对象会抛出异常
public class User { String name; String password; public User() { } public User(String name, String password) { this.name = name; this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; } }
对 UserService 9003 项目改造
@FeignClient("SERIVCE-USER") @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); }
在 feign-consumer 9010 项目添加调用
@GetMapping(value = "/feign-consumer2") public String helloConsumer2(){ StringBuilder sb = new StringBuilder(); sb.append(helloService.hello()).append("\n"); sb.append(helloService.hello("DIDI")).append("\n"); sb.append(helloService.hello("DIDI","123456")).append("\n"); sb.append(helloService.hello(new User("DIDI","admin"))).append("\n"); return sb.toString(); }
访问 http://localhost:9010/feign-consumer2 可获取 Hello World 9003 Hello DIDI User{name=‘DIDI’, password=‘123456’} Hello DIDI, admin
继承特性
创建名为 user-service-api 的 maven 项目
pom 依赖
<?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">
<modelVersion/>
<groupId>com.zk.springcloud</groupId>
<artifactId>springcloud-user-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
复制上节中的 User 对象到 user-service-api 工程
创建 HelloService 接口
package com.zk.springcloud; import org.springframework.web.bind.annotation.*; @RequestMapping("/refactor") public interface HelloService { @GetMapping(value = "/hello4") String hello(@RequestParam("name") String name); @GetMapping(value = "/hello5") User hello(@RequestHeader("name") String name,@RequestHeader("password") String password); @PostMapping(value = "/hello6") String hello(@RequestBody User user); }
对 user-service 和 feign-consumer 重构
对 user-service 重构
添加 user-serivce-api的依赖
<dependency>
<groupId>com.zk.springcloud</groupId>
<artifactId>springcloud-user-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
创建 RefactorHelloController 类继承 user-service-api 中定义的 HelloService 接口
package com.zk.springcloud.service; import com.zk.springcloud.HelloService; import com.zk.springcloud.User; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class RefactorHelloController implements HelloService { @Override public String hello(@RequestParam("name") String name) { return "Hello " + name; } @Override public User hello(@RequestHeader("name") String name,@RequestHeader("password") String password) { return new User(name,password); } @Override public String hello(@RequestBody User user) { return "Hello " + user.getName() + ", " + user.getPassword(); } }
对 feign-consumer 重构
创建 RefactorHelloService 接口
import org.springframework.stereotype.Service; @FeignClient(value = "SERIVCE-USER") @Service public interface RefactorHelloService extends HelloService { }
添加调用
@GetMapping(value = "/feign-consumer3") 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.zk.springcloud.User("MIMI","123456"))).append("\n"); return sb.toString(); }
访问 http://localhost:9010/feign-consumer3 获得如下结果
Hello MIMI User{name='MIMI', password='123456'} Hello MIMI, 123456
Ribbon 配置
全局配置
ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000
指定服务配置
serivce-user.ribbon.ConnectTimeout=500
serivce-user.ribbon.ReadTimeout=2000
serivce-user.ribbon.okToRetryOnAllOperations=true
serivce-user.ribbon.MaxAutoRetriesNextServer=2
serivce-user.ribbon.MaxAutoRetries=1
重试机制
feign-consumer 添加上述指定服务配置
user-service 修改如下超时
@GetMapping(value = "/hello") public String hello() throws InterruptedException { int sleepTime = new Random().nextInt(3000); log.info("sleepTime: " + sleepTime); Thread.sleep(sleepTime); return "Hello World 9003"; }
访问 http://localhost:9010/feign-consumer
在 user-service 可以看到重试日志
Hystrix 配置
全局配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
#关闭 Hystrix 功能
#feign.hystrix.enabled=false
#关闭熔断功能
#hystrix.command.default.execution.timeout.enabled=false
禁用 Hystrix
全局关闭 Hystrix
#关闭 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 { ··· }
指定命令配置
针对尝试机制中对 /hello 接口的熔断时间的配置可通过如下配置
hystrix.command.hello.execution.isolation.thread.timeoutInMilliseconds=5000
服务降级配置
对 feign-consumer 工程进行改造
package com.zk.springcloud.feign; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; @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); }
配置文件开启 feign.hystrix
feign.hystrix.enabled=true
将类 DisableHystrixConfiguration 注释
将 user-service 服务关闭,访问 http://localhost:9010/feign-consumer2 获得如下结果:
error error User{name='未知', password='0'} error
其他配置
请求压缩
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
日志配置
logging.level.com.zk.springcloud.feign.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 { @Bean Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } }
调用 http://localhost:9010/feign-consumer
请求详细日志
2018-10-08 14:49:34.051 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] ---> GET http://SERIVCE-USER/hello HTTP/1.1
2018-10-08 14:49:34.051 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] ---> END HTTP (0-byte body)
2018-10-08 14:49:34.872 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] <--- HTTP/1.1 200 (821ms)
2018-10-08 14:49:34.872 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] content-length: 16
2018-10-08 14:49:34.873 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] content-type: text/plain;charset=UTF-8
2018-10-08 14:49:34.873 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] date: Mon, 08 Oct 2018 06:49:34 GMT
2018-10-08 14:49:34.873 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello]
2018-10-08 14:49:34.873 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] Hello World 9003
2018-10-08 14:49:34.873 DEBUG 21632 --- [-SERIVCE-USER-5] com.zk.springcloud.feign.HelloService : [HelloService#hello] <--- END HTTP (16-byte body)
还没有评论,来说两句吧...