Skip to content

8. OpenFeign

8.1 介绍

github官网 Spring-Cloud官网2

作为Spring Cloud的子项目之一,Spring Cloud OpenFeign以将OpenFeign集成到Spring Boot应用中的方式,为微服务架构下服务之间的调用提供了解决方案。首先,利用了OpenFeign的声明式方式定义Web服务客户端;其次还更进一步,通过集成RibbonEureka实现负载均衡的HTTP客户端。

在Spring Cloud OpenFeign中,除了OpenFeign自身提供的标注(annotation)之外,还可以使用JAX-RS标注,或者Spring MVC标注。下面还是以OpenFeign标注为例介绍用法。

OpenFeign的标注@FeignClient和@EnableFeignClients

OpenFeign提供了两个重要标注@FeignClient和@EnableFeignClients。

@FeignClient标注用于声明Feign客户端可访问的Web服务。

@EnableFeignClients标注用于修饰Spring Boot应用的入口类,以通知Spring Boot启动应用时,扫描应用中声明的Feign客户端可访问的Web服务。

@EnableFeignClients标注的参数

  • value, basePackages (默认{})
  • basePackageClasses (默认{})
  • defaultConfiguration (默认{})
  • clients (默认{})

@FeignClient标注的参数

  • name, value (默认""),两者等价
  • qualifier (默认"")
  • url (默认"")
  • decode404 (默认false)
  • configuration (默认FeignClientsConfiguration.class)
  • fallback (默认void.class)
  • fallbackFactory (默认void.class)
  • path (默认"")
  • primary (默认true)

@FeignClient标注的configuration参数

@FeignClient标注的configuration参数,默认是通过FeignClientsConfiguration类定义的,可以配置Client,Contract,Encoder/Decoder等。

FeignClientsConfiguration类中的配置方法及默认值如下:

  • feignContract: SpringMvcContract
  • feignDecoder: ResponseEntityDecoder
  • feignEncoder: SpringEncoder
  • feignLogger: Slf4jLogger
  • feignBuilder: Feign.Builder
  • feignClient: LoadBalancerFeignClient(开启Ribbon时)或默认的HttpURLConnection

定制@FeignClient标注的configuration类

@FeignClient标注的默认配置类为FeignClientsConfiguration,我们可以定义自己的配置类如下:

java
@Configuration
public class MyConfiguration {

	@Bean
	public Contract feignContract(...) {....}

	@Bean
	public Encoder feignEncoder() {...}

	@Bean
	public Decoder feignDecoder() {...}
	...
}

然后在使用@FeignClient标注时,给出参数如下:

java
@FeignClient(name = "myServiceName", configuration = MyConfiguration.class, ...)
public interface MyService {

    @RequestMapping("/")
    public String getName();
    ...
}

定制@FeignClient标注的configuration类还可以有另一个方法,直接配置application.yaml文件即可,示例如下:

yaml
feign:
  client:
	config:
	  feignName: myServiceName
		connectTimeout: 5000
		readTimeout: 5000
		loggerLevel: full
		encoder: com.example.MyEncoder
		decoder: com.example.MyDecoder
		contract: com.example.MyContract

8.2 feign 和OpenFeign的区别

Feign

Feign是Springcloud组件中的一个轻量级Restful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务

OpenFeign

OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

8.3 OpenFeign的使用

8.3.1创建modules(子项目)

类型
groupIdorg.jshand (与父项目保持一致即可)
artifactId10-cloud-customer-openfeign-80 (也可随意)

8.3.2 修改pom.xml

xml
<?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>springcloud-202101</artifactId>
        <groupId>org.jshand</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>10-cloud-customer-openfeign-80</artifactId>
    
    <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-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </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>
        </dependency>
    </dependencies>

</project>

8.3.3 application.yaml配置文件

yaml
server:
  port: 80

spring:
  application:
    name: cloud-member-center-service

eureka:
  client:
    serviceUrl:
      defaultZone: http://server8760.com:8760/eureka
  instance:
    hostname: 127.0.0.1
    instance-id: order-80
    prefer-ip-address: true

8.3.4 主启动类

在主启动类上添加@EnableFeignClients注解,激活使用@FeignClient注解标注的Feign客户端。

java
package org.jshand.cloud;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * 使用OpenFeign作为客户端
 */
@SpringBootApplication
@Slf4j
@EnableFeignClients
public class MemberCenterOpenFeignApp {

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

}

8.3.5 定义Feign接口

定义调用远程服务的接口,并声明 @FeignClient注解,其中name或者value为微服务名称(即注册中心的服务名)

java
package org.jshand.cloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 定义OpenFeign客户端接口
 */
@Component
@FeignClient(name="PAYMENT")
public interface PaymentService {

    @RequestMapping(value = "/echo/{string}")
    public String echo(@PathVariable String string);
}

8.3.6 定义Controller调用Feign接口

java
package org.jshand.cloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.jshand.cloud.service.PaymentService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class MemberCenterController {

    @Resource
    PaymentService paymentService;

    /**
     * http://127.0.0.1:80/openfeign/consumer/echo/abc
     * @param string
     * @return
     */
    @RequestMapping(value = "/openfeign/consumer/echo/{string}")
    public String echo(@PathVariable String string){
        return paymentService.echo(string);
    }

}

8.3.7 浏览器测试

使用地址 http://127.0.0.1:80/openfeign/consumer/echo/abc 进行测试,能够正常访问微服务,并提供负载均衡的特性。截图略

8.4 OpenFeign超时控制

默认OpenFeign客户端等待时间为1秒钟,如果服务端超过1秒钟没有返回,则客户端直接抛出Timeout,为了避免这样的情况,有时候我们需要处理Feign客户端的超时控制。

为了模拟上述问题,在服务端控制器方法中添加如下代码(如果是集群,需要每个应用都加),让服务端处理时间延长

java
try {
	TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
	e.printStackTrace();
}

添加完上述代码后,浏览器单独访问服务端成功没有问题,但是使用OpenFeign客户端调用时报如下错误

为了解决上述问题,此时需要配置客户端。在客户端配置Feign客户端超时时间。其他OpenFeign配置参考链接

yaml
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000

或者直接设置Ribbon的超时时间, 其他配置请参考连接

yaml
ribbon:
  ConnectTimeout: 8000
  ReadTimeout: 8000

8.5 日志打印功能

OpenFeign提供了打印日志功能,提供了如下级别。开发过程中可以通过配置来调整日志级别,从而了解OpenFeign在调用http请求的过程中的细节。

  • NONE: 默认的,不显示任何日志

  • BASIC: 仅记录请求方法、URL、响应状态码以及执行时间

  • HEADERS:除了BASIC 中自定义的信息外,还有请求和响应的信息头

  • FULL: 除了HEADERS中定义的信息外, 还有请求和响应的正文以及元数据。

以Full类型为例,需要做如下功能:

8.5.1 定义配置类

定义配置类,用于提供日志类型

java
package org.jshand.cloud.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;
    }
}

8.5.2 配置日志级别

在application.yml中配置FeignClient客户端类的日志级别

yaml
logging:
  level:
    org.jshand.cloud.service.PaymentService: debug

测试后控制台打印如下日志

Released under the MIT License.