OpenFeign服务接口调用

一、前世今生

Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Ribbon 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Feign。 Feign 对 Ribbon 进行了集成,利用 Ribbon 维护了一份可用服务清单,并通过 Ribbon 实现了客户端的负载均衡。Feign 是一种声明式服务调用组件,它在 RestTemplate 的基础上做了进一步的封装。通过 Feign,我们只需要声明一个接口并通过注解进行简单的配置(类似于 Dao 接口上面的 Mapper 注解一样)即可实现对 HTTP 接口的绑定。

通过 Feign,我们可以像调用本地方法一样来调用远程服务,而完全感觉不到这是在进行远程调用。Feign 支持多种注解,例如 Feign 自带的注解以及 JAX-RS 注解等,但遗憾的是 Feign 本身并不支持 Spring MVC 注解,这无疑会给广大 Spring 用户带来不便。2019 年 Netflix 公司宣布 Feign 组件正式进入停更维护状态,于是 Spring 官方便推出了一个名为 OpenFeign 的组件作为 Feign 的替代方案。

二、OpenFeign

OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign。OpenFeign 是 Spring Cloud 对 Feign 的二次封装,它具有 Feign 的所有功能,并在 Feign 的基础上增加了对 Spring MVC 注解的支持,例如 @RequestMapping、@GetMapping 和 @PostMapping 等。

springcloud官方文档参考:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign

GitHub参考文档:https://github.com/spring-cloud/spring-cloud-openfeign

OpenFeign 常用注解

使用 OpenFegin 进行远程服务调用时,常用注解如下表。 

注解说明
@FeignClient  该注解用于通知 OpenFeign 组件对 @RequestMapping 注解下的接口进行解析,并通过动态代理的方式产生实现类,实现负载均衡和服务调用。
@EnableFeignClients 该注解用于开启 OpenFeign 功能,当 Spring Cloud 应用启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口,生成代理并注册到 Spring 容器中。
@RequestMapping Spring MVC 注解,在 Spring MVC 中使用该注解映射请求,通过它来指定控制器(Controller)可以处理哪些 URL 请求,相当于 Servlet 中 web.xml 的配置。
@GetMapping Spring MVC 注解,用来映射 GET 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.GET) 。
@PostMapping Spring MVC 注解,用来映射 POST 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.POST) 。

Spring Cloud Finchley 及以上版本一般使用 OpenFeign 作为其服务调用组件。由于 OpenFeign 是在 2019 年 Feign 停更进入维护后推出的,因此大多数 2019 年及以后的新项目使用的都是 OpenFeign,而 2018 年以前的项目一般使用 Feign。但是目前OpenFeign也进入了维护阶段

三、Feign和OpenFeign 对比

下面我们就来对比下 Feign 和 OpenFeign 的异同。

相同点

Feign 和 OpenFegin 具有以下相同点:

  • Feign 和 OpenFeign 都是 Spring Cloud 下的远程调用和负载均衡组件。
  • Feign 和 OpenFeign 作用一样,都可以实现服务的远程调用和负载均衡。
  • Feign 和 OpenFeign 都对 Ribbon 进行了集成,都利用 Ribbon 维护了可用服务清单,并通过 Ribbon 实现了客户端的负载均衡。
  • Feign 和 OpenFeign 都是在服务消费者(客户端)定义服务绑定接口并通过注解的方式进行配置,以实现远程服务的调用。

不同点

Feign 和 OpenFeign 具有以下不同:

  • Feign 和 OpenFeign 的依赖项不同,Feign 的依赖为 spring-cloud-starter-feign,而 OpenFeign 的依赖为 spring-cloud-starter-openfeign。
  • Feign 和 OpenFeign 支持的注解不同,Feign 支持 Feign 注解和 JAX-RS 注解,但不支持 Spring MVC 注解;OpenFeign 除了支持 Feign 注解和 JAX-RS 注解外,还支持 Spring MVC 注解。

四、案例演示

4.1.新建cloud-consumer-feign-order80

创建maven项目

 设置模块名称

4.2.设置POM

在pom中引入依赖:

<dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.augus.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

4.2.设置配置文件

创建application.yml配置文件,内容如下:

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      # 将自己注册到两个eureka集群中
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

4.3.设置主启动类

主要是需要添加上@EnableFeignClients 这个注解,表示使用OpenFeign

package com.augus.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderFeignMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderFeignMain80.class,args);
    }
}

4.4.业务类

业务逻辑接口+@FeignClient配置调用provider服务

创建service包,在下面创建 PaymentFigenService 接口,内容如下:

package com.augus.cloud.service;

import com.augus.cloud.pojo.CommonResult;
import com.augus.cloud.pojo.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //指定调用的服务
public interface PaymentFigenService {

    //指定调用的服务的那个接口:直接复制服务提供者的接口即可
    @GetMapping(value = "/payment/get/{id}")
    CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);

    //指定调用的服务的那个接口
    @PostMapping(value = "/payment/create")
    CommonResult<Payment> create(@RequestBody Payment payment);

}

注意:上述接口需要通过 @FeignClient(value = "CLOUD-PAYMENT-SERVICE") 指定调用的是那个服务,只写服务名,不需要http://,切记切记这里一定注意,同时这里的接口实际上指定的就是去调用服务提供者的那个接口,所以将服务提供者的controller中的接口直接复制进来即可

4.5.创建controller

在下面创建 OrderFeigenController 内容如下:

package com.augus.cloud.controller;

import com.augus.cloud.pojo.CommonResult;
import com.augus.cloud.pojo.Payment;
import com.augus.cloud.service.PaymentFigenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class OrderFeigenController {
    @Autowired
    private PaymentFigenService paymentFigenService;

    //指定调用的服务接口
    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult<Payment> create(@PathVariable("id") Long id){
        return paymentFigenService.getPaymentById(id);
    }

    //指定调用的服务接口
    @PostMapping(value = "/consumer/payment/create")
    CommonResult<Payment> create(@RequestBody Payment payment){
        return paymentFigenService.create(payment);
    }
}

4.6.测试

访问:http://localhost/consumer/payment/get/1,会发现8001和8002轮询出现,因为OpenFeign里面包含的ribbon,所以直接就有服务间的负载均衡

五、OpenFeign超时控制

OpenFeign在进行调用服务提供者的时候,他的默认等待时间,只有1s,如果超过1s所调用的服务还没有返回信息,就会报错,下面呢,我们将通过案例来演示,解决超时控制

5.1.在服务提供方8001、8002controller中添加方法进行模拟

在8001和8002controller中添加的内容如下(后续直接复制即可):

@GetMapping("/payment/feign/timeout")
    public String paymentFeignTimeout() throws InterruptedException {
        //设置暂停3s,模拟超时
        TimeUnit.SECONDS.sleep(3);
        return serverPort;
    }

5.2.在服务消费方cloud-consumer-feign-order80添加超时方法

在 PaymentFeignService下添加接口,内容如下:

 @GetMapping("/payment/feign/timeout")
    String paymentFeignTimeout();

如图所示:

5.3.在cloud-consumer-feign-order80的controller中添加内容:

在 OrderFeigenController 中添加方法:

@GetMapping("/consumer/payment/feign/timeout")
    public String paymentFeignTimeout(){
        //OpenFeign调用默认只有1s的等待时间,如果超时还获取不到结果会报错
        return paymentFigenService.paymentFeignTimeout();
    }

5.4.测试

浏览器访问:http://localhost/consumer/payment/feign/timeout  这里会报超时错误原因在于 OpenFeign默认等待1秒钟,超过后报错

5.5.设置超时控制

上面的问题,在我们后续的开发中,有的服务由于本身的原因调用时间会超过1s,这时候报错肯定不行,就需要进行设置,避免出现错误,那么设置如下,只需要在cloud-consumer-feign-order80的application.yml中加入如下内容:

# 设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
  # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
  # 指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

注意:上面设置的内容是对于Ribbon的设置,因为在OpenFeign中引入后,里面就包含了ribbon,所以虽然说的是设置的OpenFeign超时控制,但是实际上设置的是Ribbon,依赖关系如下图:

设置后重启 cloud-consumer-feign-order80 服务,然后访问:http://localhost/consumer/payment/feign/timeout,发现不会在报错,会出现如下内容:

六、OpenFeign日志打印功能

6.1.OpenFeign日志打印功能

在通过OpenFeign调用服务进行联调的时候,有时候需要详细的了解OpenFeign的调用,如头、状态码、时间、接口等等;OpenFeign就自身就提供了一个详细的日志打印功能,可以通过配置来调整日志级别,从而了解Feign中Http请求的细节,也就是对Feign接口的调用情况进行监控和输出。

6.2.日志级别

在OpenFeign中日志分为如下级别:

级别 描述
NONE 默认的,不显示任何日志;
BASIC 仅记录请求方法、URL、响应状态码及执行时间;
HEADERS 除了BASIC中定义的信息之外,还有请求和响应的头信息;
 FULL 除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

6.3.配置日志bean

在com.augus.cloud包下创建子包config,在里面创建 FeignConfig.java  内容如下:

package com.augus.cloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {
    /**
     * feignClient配置日志级别
     *
     * @return
     */
    @Bean
    public Logger.Level feignLoggerLevel() {
        // 设置日志的级别为:FULL (请求和响应的头信息,请求和响应的正文及元数据)
        return Logger.Level.FULL;
    }
}

6.4.在cloud-consumer-feign-order80的配置文件中开启日志配置

在application.yml中添加如下内容:

logging:
  level:
    # 设置以什么级别监视那个接口
    com.augus.cloud.service.PaymentFeignService: debug

6.5.测试

重启 cloud-consumer-feign-order80 访问:http://localhost/consumer/payment/get/1  在控制台即可看到打印出的信息:

posted @ 2023-01-03 15:08  酒剑仙*  阅读(229)  评论(0)    收藏  举报