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 {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }

}
@FeignClient(name="HELLO_SERVICE",configuration = FullLogConfiguration.class
public interface HelloService{
	...
}

Feign的Logger级别:
NONE
BASIC
HEADERS
FULL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值