SpringCloud之Feign中的继承、日志、压缩

上篇文章和大家分享了声明式微服务调用组件 Feign 的基本用法,相信大家已经了解到使用 Feign 的好处了,使用 Feign 有效地解决了使用 RestTemplate 时的代码模板化的问题,使服务之间的调用更加简单方便,同时也不易出错。不过,细心的读者可能也发现,上篇文章中我们学的 Feign 还是有一些明显的缺陷,例如,当我们在 provider 中定义接口时,可能是下面这样:

@RestController
public class GirlController {
    @GetMapping("/girl")
    public String girl(String name) {
        return "love " + name + " !";
    }
}

然后在feign-consumer中定义:

@FeignClient("provider")
public interface GirlService {
    @GetMapping("/girl")
    String gril(@RequestParam("name") String name);
}

可以看到provider 和 feign-consumer代码明显重复了,而且如果调用的参数和提供的参数不一致那么就会报错,如果不细心的话,难免会发生这样的事情。那么如何避免这样的事情发生呢?那么我们可以使用feign的继承。

准备工作

首先我们创建一个FeignAdvanced的普通maven项目,作为父项目,然后我们在父项目中创建一个eureka的SpringBoot项目作为子模块,关于创建eureka项目,我这里就不再多叙述了,因为非常的简单,大家可以参考我之前写的文章。
创建好eureka项目后,我们再创建一个commons的普通maven工程,作为子模块。
它的依赖如下:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.6.RELEASE</version>
        </dependency>
    </dependencies>

就一个简单的web依赖,然后我们在commons项目中创建一个GirlService的接口:

public interface GirlService {
  @GetMapping("/girl")
  String girl(@RequestParam String name);
}

在这个 GirlService 接口中,我们会将前面提到的 provider 和 feign-consumer 中公共的部分抽取出来定义在这里,然后在 provider 中调用这个接口,在 feign-consumer 中实现这个接口。

这个接口定义好了后,我们就要去实现我们的继承。

继承性

Feign 中继承,一共两个步骤:

  1. 在provider中实现公共接口
  2. 在feign-consumer中去调用provider中的接口

在provider中实现接口

然后我们创建一个provider的SpringBoot项目作为子项目,provider具体创建,我这里也不详细的说了,很简单,可以参考我前面的文章。创建好provider项目后我们将我们的commons加入依赖:

 <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.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>cn.com.scitc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

上面就是provider的依赖,然后我们需要将provider注册到eureka中,这一步很简单,可以参考我前面的文章。

然后我们在provider中定义一个GirlController 来 实现commons中定义的GirlService接口:

@RestController
public class GirlController  implements GirlService {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public String girl(String name) {
        log.info("provider提供girl的服务");
        return "love" + name + "!";
    }
}

这样就完成了我们的provider接口的开发。

在feign-consumer中调用接口

我们再创建一个feign-consumer的SpringBoot项目,作为子模块,它的依赖如下:

<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-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>cn.com.scitc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

然后修改配置文件,将feign-consumer注册到eureka上,这里也不多做叙述。然后我们需要在feign-consumer的启动类加入@EnableFeignClients 注解,来开启Feign的支持:

@SpringBootApplication
@EnableFeignClients
public class FeignConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApplication.class, args);
    }
}

然后我们在feign-consumer中添加一个FeignGirlService 接口 并继承 commons 依赖中的GirlService接口,如下:

@FeignClient("provider")
public interface FeignGirlService extends GirlService {
}

需要注意的是,这里的 FeignGirlService 接口直接继承自 GirlSerivce ,继承之后, FeignGirlService 自动具备了 GirlSerivce 中的接口,因此可以在使用 @FeignClient(“provider”) 注解绑定服务之后就可以直接使用了。

然后我再从feign-consumer中定一个LoveGirlController,在 LoveGirlController 中使用 FeignGirlService:

@RestController
public class LoveGirlController {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    FeignGirlService girlService;

    @GetMapping("/girl")
    public String girl(String name) {
        log.info("consumer调用了provider提供的girl服务");
        return girlService.girl(name);
    }
}

然后我们启动eureka、provider、feign-consumer ,去访问我们的服务,调用结果如下:
在这里插入图片描述

优缺点分析

这种写法和我们以前的写法有什么区别,有什么缺点和优点?

  1. 使用继承特性,代码简洁明了,不易出错,不必担心接口返回值是否写对,接口地址是否写对。如果接口地址有变化,也不用 provider 和 feign-consumer 大动干戈,只需要修改 commons 模块即可,provider 和 feign-consumer 就自然变了;
  2. 前面提到的在 feign-consumer 中绑定接口时,如果是 key/value 形式的参数或者放在 header 中的参数,就必须要使用 @RequestParam 注解或者 @RequestHeader 注解,这个规则在这里一样适用。即在 commons 中定义接口时,如果涉及到相关参数,该加的@RequestParam 注解或者 @RequestHeader 注解一个都不能少;
  3. 当然,使用了继承特性也不是没有缺点。继承的方式将 provider 和 feign-consumer 绑定在一起,代码耦合度变高,一变俱变,此时就需要严格的设计规范,否则会牵一发而动全身,增加项目维护的难度。

日志配置

我们使用了Feign,如果想要看微服务之前的调用情况,那么就可以使用Feign的日志功能
,Feign的日志功能有四种:
NONE ,不开启日志记录,默认即此
BASIC ,记录请求方法和请求 URL ,以及响应的状态码以及执行时间
HEADERS ,在第2条的基础上,再增加请求头和响应头
FULL ,在第3条的基础上再增加 body 以及元数据

我们一般使用最强的就是 FULL了。

那么如何使用Feign的日志功能呢?非常的简单,只需要在启动类加一个bean就可以了如下:

@SpringBootApplication
@EnableFeignClients
public class FeignConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApplication.class, args);
    }

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

在这里插入图片描述

这里我们选择FULL 最强的,然后在application.yml或者application.properties中配置,这里我使用的是yml

logging:
  level:
    cn:
      com:
        scitc:
          FeignGirlService: debug

这里 logging.level 是指日志级别的前缀,cn.com.scitc.FeignGirlService.FeignGirlService表示该 class 以 debug 级别输出日志。当然,类路径也可以是一个 package ,这样就表示该 package 下的所有 class 以 debug 级别输出日志。配置完成后,重启 feign-consumer 项目,访问其中任意一个接口,就可以看到请求日志,如下:

在这里插入图片描述

数据的压缩

数据的压缩,主要是解决传输效率,具体配置如下:

feign:
  compression:
    request:
      enabled: true
      mime-types: text/html,application/json
      min-request-size: 2048
    response:
      enabled: true

前两行表示开启请求和响应压缩,第三行表示压缩的数据类型,默认是 text/html,application/json,application/xml, 第四行表示压缩数据的下限,即当要传输的数据大于2048时才需要对请求进行压缩。

请求重试

当Feign出现问题的时候,我们可以尝试重新连接,前面我们使用的是Spring-retry 的依赖,但是在Feign中自带了请求重试功能,直接配置就可以使用:

ribbon:
  MaxAutoRetries: 3
  MaxAutoRetriesNextServer: 1
  OkToRetryOnAllOperations: false

其中MaxAutoRetries 代表最大的请求次数
其中MaxAutoRetriesNextServer代表最大重试的service个数
其中OkToRetryOnAllOperations代表是否开启任何异常都重试

那么我们也可以针对一个微服务进行请求重试的配置:

provider:
  ribbon:
    MaxAutoRetries: 3
    MaxAutoRetriesNextServer: 1
    OkToRetryOnAllOperations: false

这个配置就是针对服务名称是provider的服务,注意这里的provider服务名字是spring.application.name中的名称。

我们也可以不通过配置文件来配置,直接用一个bean:

 @Bean
    public Retryer feignRetryer() {
        Retryer.Default retryer = new Retryer.Default();
        return retryer;
    }

总结

本文主要向大家介绍了声明式微服务调用工具 Feign 的一些高级特性,例如继承机制、日志配置、请求压缩、请求重试等,并对继承特性的优缺点进行了分析。在实际开发中,灵活地使用这些属性,可以使我们的微服务以一个更高的效率运行。通过对这些特性的学习,相信大家对 Feign 将会有一个更深刻的认识。

源码地址

github

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值