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;
    }
    时刻与技术进步,每天一点滴,日久一大步!!! 本博客只为记录,用于学习,如有冒犯,请私信于我。
  • 相关阅读:
    5 Things Every Manager Should Know about Microsoft SharePoint 关于微软SharePoint每个经理应该知道的五件事
    Microsoft SharePoint 2010, is it a true Document Management System? 微软SharePoint 2010,它是真正的文档管理系统吗?
    You think you use SharePoint but you really don't 你认为你使用了SharePoint,但是实际上不是
    Introducing Document Management in SharePoint 2010 介绍SharePoint 2010中的文档管理
    Creating Your Own Document Management System With SharePoint 使用SharePoint创建你自己的文档管理系统
    MVP模式介绍
    权重初始化的选择
    机器学习中线性模型和非线性的区别
    神经网络激励函数的作用是什么
    深度学习中,交叉熵损失函数为什么优于均方差损失函数
  • 原文地址:https://www.cnblogs.com/myitnews/p/13654985.html
Copyright © 2011-2022 走看看