zoukankan      html  css  js  c++  java
  • 声明式服务调用Feign

    开发微服务,免不了需要服务间调用。Spring Cloud框架提供了RestTemplate和FeignClient两个方式完成服务间调用(注:早期我们用的是叫 Netflix Feign,不过这个东西的最近一次更新还停留在 2016年7月,OpenFeign 则是 Spring Cloud 团队在 Netflix Feign 基础上开发出来的声明式服务调用组件,OpenFeign也一直在维护)。

    使用RestTemplate时,每次都要写请求 Url 、配置响应数据类型,最后还要组装参数,更重要的是这些都是一些重复的工作,代码高度相似,每个请求只有 Url 不同,请求方法不同、参数不同,其它东西基本都是一样的。

    一、Feign基本使用

    准备工作

    搭建一个父工程,然后创建一个服务注册中心。服务注册中心搭建成功后,接下来我们还要再搭建一个 provider 用来提供服务。provider 搭建成功后,依然提供一个 HelloController 接口,里边配上一个 /hello的接口:

    @RestController
    public class HelloController {
        @GetMapping("/hello")
        public String hello(String name) {
            return "hello " + name + " !";
        }
    }

    然后分别启动服务注册中心 eureka 以及服务提供者 provider ,然后在浏览器中输入http://localhost:8761 可以看到我们的实例情况。

    如何使用feign

    准备工作完成后,我们创建一个feign-consumer的SpringBoot工程,项目创建好后依赖如下 :

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--openfeign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    创建好了后,我们在application.yml中将我们的feign -consumer 注册到服务中心 (eureka)中:

    spring:
      application:
        name: feigin-consumer
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka
    server:
      port: 5002

    最后在我们的feign-consumer中的主启动类中添加@EnableFeignClients注解,开启Feign的支持。

    下面我们创建一个HelloServiceFeign接口,用来消费provider提供的接口 。

    @FeignClient("service-provider")
    public interface HelloServiceFeign {
        @GetMapping("/hello")
        String hello(@RequestParam("name") String name);
    }

    这个接口做了两件事情:

    • 使用 @FeignClient(“service-provider”) 注解将当前接口和 provider 服务绑定, provider 是服务名,可以忽略大小写;
    • 使用 SpringMVC 的 @GetMapping("/hello") 注解将 hello 方法和 provider 中的 hello 接口绑定在一起。

    经过这样的步骤之后,我们就可以在一个 Controller 中注入 HelloServiceFeign 接口并使用它了,而 HelloServiceFeign 接口也会去调用相关的服务。我的 Controller 如下:

    @RestController
    public class HelloController {
        @Autowired
        HelloServiceFeign helloService;
    
        @GetMapping("/hello")
        public String hello(String name) {
            return helloService.hello(name);
        }
    }

    配置好了后,我们在浏览器上访问http://localhost:5002/hello?name=SpringCloud,显示的效果如下:

    可以看到我们这样写代码 比之前用restTemplate 清爽了许多。

     

    二、Feign的继承、日志、压缩

    上节我们学的 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的继承。

    继承

    将创建一个common模块,存放公共的接口。如下:创建一个GirlService的接口

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

    provider实现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 + "!";
        }
    }

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

    我们使用了Feign,如果想要看微服务之前的调用情况,那么就可以使用Feign的日志功能。

    Feign的日志功能有四种:

    • NONE ,不开启日志记录,默认即此
    • BASIC ,记录请求方法和请求 URL ,以及响应的状态码以及执行时间
    • HEADERS ,在第2条的基础上,再增加请求头和响应头
    • FULL ,在第3条的基础上再增加 body 以及元数据

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

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

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

    这里我们选择FULL 最强的,然后在application.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代表是否开启任何异常都重试。

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

    service-provider: --- 服务提供者应用名
      ribbon:
        MaxAutoRetries: 3
        MaxAutoRetriesNextServer: 1
        OkToRetryOnAllOperations: false

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

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

    @Bean
    public Retryer feignRetryer() {
    	Retryer.Default retryer = new Retryer.Default();
    	return retryer;
    }
    时刻与技术进步,每天一点滴,日久一大步!!! 本博客只为记录,用于学习,如有冒犯,请私信于我。
  • 相关阅读:
    swift 加载 storyboard 里的UIViewController
    ios 下创建,删除文件夹的方法
    iOS ARC模式 内存管理
    UISwitch
    建立 svn 服务端
    去除NSString里面的空格
    正则表达式验证邮箱的合法性
    memcached 的安装与使用
    windows2008 IIS下配置FTP服务
    lua语法备忘录
  • 原文地址:https://www.cnblogs.com/myitnews/p/13654985.html
Copyright © 2011-2022 走看看