目录

spirngcloud服务调用OpenFeign

概述

spring cloud openfeign

6.1. Declarative REST Client: Feign

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.

Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解,比如:FeignClient注解。Feign有可插拔的注解,包括Feign注解和JAX-RS注解。Feign也支持编码器和解码器,Spring Cloud Open Feign对Feign进行增强支持Spring MVC注解,可以像Spring Web一样使用HttpMessageConverters等。

Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。

功能可插拔的注解支持,包括Feign注解和JAX-RS注解。支持可插拔的HTTP编码器和解码器(Gson,Jackson,Sax,JAXB,JAX-RS,SOAP)。支持Hystrix和它的Fallback。支持Ribbon的负载均衡。支持HTTP请求和响应的压缩。灵活的配置:基于 name 粒度进行配置支持多种客户端:JDK URLConnection、apache httpclient、okhttp,ribbon)支持日志支持错误重试url支持占位符可以不依赖注册中心独立运行

可以类比Mybatis的@Mapper注解使用Feign,以前是在Dao接口上标注@Mapper注解,现在是在一个微服务接口上标注一个FeignClient注解,即可完成对服务提供方的接口绑定,底层和mybatis一样都是使用动态代理。

Feign和OpenFeign区别

http://img.cana.space/picStore/20201110015848.png

使用演示

  1. 新建工程cloud-consumer-feign-order80

  2. 引入feign依赖

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>org.eh</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
       
        <artifactId>cloud-consumer-feign-order80</artifactId>
       
        <dependencies>
            <!--SpringBoot整合openfeign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.eh</groupId>
                <artifactId>cloud-common</artifactId>
                <version>${project.version}</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.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>
       
    </project>
    
  3. YML

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    server:
      port: 80
    eureka:
      client:
        # 只需要从注册中心拉取服务地址列表即可,无需向注册中心注册自己
        register-with-eureka: false
        fetch-registry: true
        service-url:
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
       
    
  4. 主启动,激活fegin功能,添加@EnableFeignClients注解

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    package com.eh.cloud2020.order;
       
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
       
    @EnableFeignClients
    @SpringBootApplication
    public class OrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class, args);
        }
    }
    
  5. 编写绑定服务提供者的接口

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    package com.eh.cloud2020.order.service;
       
       
    import com.eh.cloud2020.common.entity.CommonResult;
    import com.eh.cloud2020.common.entity.Payment;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
       
    @FeignClient(value = "CLOUD-PAYMENT-SERVICE")
    public interface PaymentFeignService {
       
        @GetMapping(value = "/payment/{id}")
        CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
    }
    
  6. 编写Controller调用service实现调用远程服务

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    package com.eh.cloud2020.order.controller;
       
    import com.eh.cloud2020.common.entity.CommonResult;
    import com.eh.cloud2020.common.entity.Payment;
    import com.eh.cloud2020.order.service.PaymentFeignService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
       
    @RestController
    @Slf4j
    public class OrderConsumerController {
       
       
        private final PaymentFeignService paymentFeignService;
       
       
        @Value("${server.port}")
        private String serverPort;
       
        public OrderConsumerController(PaymentFeignService paymentFeignService) {
            this.paymentFeignService = paymentFeignService;
        }
       
        @GetMapping("/order/payment/{id}")
        public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
            return paymentFeignService.getPaymentById(id);
        }
    }
    
  7. 验证

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    GET http://localhost/order/payment/1
       
    HTTP/1.1 200 
    Content-Type: application/json
    Transfer-Encoding: chunked
    Date: Mon, 09 Nov 2020 18:46:47 GMT
    Keep-Alive: timeout=60
    Connection: keep-alive
       
    {
      "code": 200,
      "desc": "查询订单成功,提供服务端口号:8001",
      "data": {
        "id": 1,
        "serial": "aaabbb01"
      }
    }
       
    Response code: 200; Time: 101ms; Content length: 77 bytes
       
    Cannot preserve cookies, cookie storage file is included in ignored list:
    > /Users/david/my/study/project/cloud2020/.idea/httpRequests/http-client.cookies
       
    

超时控制

超时演示

  1. 服务提供方cloud-provider-payment8001故意写暂停程序

    1
    2
    3
    4
    5
    6
    
    @SneakyThrows
    @GetMapping("/paymentTimeout")
    public String paymentTimeout() {
        TimeUnit.SECONDS.sleep(3);
        return "ok";
    }
    
  2. 服务消费方绑定远程接口中添加超时方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    @FeignClient(value = "CLOUD-PAYMENT-SERVICE")
    public interface PaymentFeignService {
       
        @GetMapping(value = "/payment/{id}")
        CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
       
        @GetMapping("/paymentTimeout")
        String paymentTimeout();
    }
    
  3. 服务消费方调用超时方法

    1
    2
    3
    4
    
    @GetMapping("/order/paymentTimeout")
    public String getPaymentById() {
        return paymentFeignService.paymentTimeout();
    }
    
  4. 测试

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    GET http://localhost/order/paymentTimeout
       
    HTTP/1.1 500 
    Content-Type: application/json
    Transfer-Encoding: chunked
    Date: Mon, 09 Nov 2020 19:26:20 GMT
    Connection: close
       
    {
      "timestamp": "2020-11-09T19:26:20.773+0000",
      "status": 500,
      "error": "Internal Server Error",
      "message": "Read timed out executing GET http://CLOUD-PAYMENT-SERVICE/paymentTimeout",
      "path": "/order/paymentTimeout"
    }
       
    Response code: 500; Time: 2016ms; Content length: 205 bytes
       
    Cannot preserve cookies, cookie storage file is included in ignored list:
    > /Users/david/my/study/project/cloud2020/.idea/httpRequests/http-client.cookies
       
    

超时控制

默认Feign客户端只等待1s钟,但是服务端处理需要超过1s钟导致Feign客户端不想等待了,直接报错。为了避免这样的情况,可以设置Feign客户端的超时控制。

OpenFeign集成了Ribbon,所以可以直接使用Ribbon的超时控制功能呢。

在yaml中配置如下:

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

再次访问

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
GET http://localhost/order/paymentTimeout

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Mon, 09 Nov 2020 19:35:56 GMT
Keep-Alive: timeout=60
Connection: keep-alive

ok

Response code: 200; Time: 1799ms; Content length: 2 bytes

Cannot preserve cookies, cookie storage file is included in ignored list:
> /Users/david/my/study/project/cloud2020/.idea/httpRequests/http-client.cookies

OpenFeign日志增强

openfeign提供了日志打印功能。

Logger有四种类型:NONE(默认)、BASIC、HEADERS、FULL,通过注册Bean来设置日志记录级别

使用日志增强功能步骤

  1. 注入配置类

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    package com.eh.cloud2020.payment.config;
       
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
       
    @Configuration
    public class FeignConfig {
       
        @Bean
        public Logger.Level feignLoggerLevel() {
            // 请求和响应的头信息 , 请求和响应的正文及元数据
            return Logger.Level.FULL;
        }
    }
    
  2. yml

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    server:
      port: 80
    eureka:
      client:
        # 只需要从注册中心拉取服务地址列表即可,无需向注册中心注册自己
        register-with-eureka: false
        fetch-registry: true
        service-url:
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
    #设置feign客户端超时时间(OpenFeign默认支持ribbon)
    ribbon:
      #指的是建立连接后从服务器读取到可用资源所用的时间
      ReadTimeout: 5000
      #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
      ConnectTimeout: 5000
       
    logging:
      level:
        # feign日志以什么级别监控哪个接口
        com.eh.cloud2020.payment.service.PaymentFeignService: debug
    
  3. 测试

    http://img.cana.space/picStore/20201110035920.png