Spring Cloud OpenFeign 入门

1、概览

本文将带你了解 Spring Boot 应用中的声明式 REST 客户端 - Spring Cloud OpenFeign

Feign 通过可插拔的注解支持(包括 Feign 注解和 JAX-RS 注解)使编写 Web 客户端更加容易。

此外,Spring Cloud 还增加了对 Spring MVC 注解和使用与 Spring Web 中相同的 HttpMessageConverter 的支持。

使用 Feign 的一个好处是,除了接口定义外,无需编写任何调用服务的代码。

2、依赖

首先,创建 Spring Boot Web 项目,并在 pom.xml 文件中添加 spring-cloud-starter-openfeign 依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

此外,还需要添加 spring-cloud-dependencies 依赖:

 <dependencyManagement>
     <dependencies>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

你可以在 Maven Central 上找到 spring-cloud-starter-openfeignspring-cloud-dependencies 的最新版本。

3、Feign 客户端

接着,需要将 @EnableFeignClients 添加到 main 类中:

@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {

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

该注解会扫描、加载应用中定义的 Feign 客户端接口组件。

然后,使用 @FeignClient 注解声明一个 Feign 客户端:

@FeignClient(value = "jplaceholder", url = "https://jsonplaceholder.typicode.com/")
public interface JSONPlaceHolderClient {

    @RequestMapping(method = RequestMethod.GET, value = "/posts")
    List<Post> getPosts();

    @RequestMapping(method = RequestMethod.GET, value = "/posts/{postId}", produces = "application/json")
    Post getPostById(@PathVariable("postId") Long postId);
}

本例配置了一个客户端来读取 JSONPlaceholder API

@FeignClient 注解中传递的 value 参数是一个强制性的任意客户端名称,而 url 参数则指定了 API 的基本 URL。

此外,由于该接口是一个 Feign 客户端,因此可以使用 Spring Web 注解来声明要调用的 API。

4、配置

每个 Feign 客户端都由一组可定制的组件组成。

Spring Cloud 会使用 FeignClientsConfiguration 类为每个已命名的客户端按需创建一个新的默认设置。

涉及的配置 Bean 如下:

  • Decoder - ResponseEntityDecoder,它封装了 SpringDecoder,用于解码 Response
  • Encoder - SpringEncoder,用于对 RequestBody 进行编码。
  • Logger - Slf4jLogger,Feign 使用的默认 logger。
  • Contract - SpringMvcContract,提供注解处理功能
  • Feign-Builder - HystrixFeign.Builder,用于构建组件
  • Client - LoadBalancerFeignClient 或默认 Feign 客户端

4.1、自定义 Bean 配置

如果想自定义其中一个或多个 Bean,可以通过创建一个 Configuration 类来覆盖它们,然后将其添加到 FeignClient 注解中:

@FeignClient(value = "jplaceholder",
  url = "https://jsonplaceholder.typicode.com/",
  configuration = ClientConfiguration.class)
public class ClientConfiguration {

    @Bean
    public OkHttpClient client() {
        return new OkHttpClient();
    }
}

在本例中,通过配置让 Feign 使用 OkHttpClient 代替默认客户端来支持 HTTP/2。

Feign 支持多种客户端以满足不同的使用情况,其中包括 ApacheHttpClient,它会随请求发送更多的 Header 信息,例如某些服务器所期望的 Content-Length

要使用这些客户端,不要忘记在 pom.xml 文件中添加所需的依赖:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

你可以在 Maven Central 上找到 feign-okhttpfeign-httpclient 的最新版本。

4.2、通过 Properties 进行配置

可以使用 application.properties 代替配置类来配置 Feign 客户端。

示例 application.yaml 如下:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

如上,将超时时间设置为 5 秒,并将应用中每个已声明客户端的 logger 日志级别设置为 basic 级别。

最后,可以通过默认的客户端名称 default 来配置所有 @FeignClient 对象,也可以为指定名称的 feign 客户端进行配置:

feign:
  client:
    config:
      jplaceholder:

如果同时定义了 Configuration Bean 和配置属性,配置属性将覆盖 Configuration Bean 的值。

5、拦截器

Feign 支持拦截器(Interceptor)。

拦截器可为每个 HTTP 请求/响应执行身份认证、日志记录等各种隐式任务。

Spring Cloud OpenFeign 预定义了一些开箱即用的拦截器,当然,也可以实现自己的拦截器。

5.1、实现 RequestInterceptor

实现 RequestInterceptor 接口,创建自定义的拦截器:

@Bean
public RequestInterceptor requestInterceptor() {
  return requestTemplate -> {
      requestTemplate.header("user", username);
      requestTemplate.header("password", password);
      requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
  };
}

如上,在拦截器中为请求添加了一些额外的 Header。

要将拦截器添加到请求链中,只需将此 Bean 添加到 Configuration 类中,或者如前所述,在 properties 文件中声明即可:

feign:
  client:
    config:
      default:
        requestInterceptors:
          com.baeldung.cloud.openfeign.JSONPlaceHolderInterceptor

5.2、使用 BasicAuthRequestInterceptor

或者,可以使用 Spring Cloud OpenFeign 提供的 BasicAuthRequestInterceptor 类:

@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor("username", "password");
}

就这么简单。现在,所有请求都将包含 Basic Authentication Header。

6、支持 Hystrix

Feign 支持 Hystrix,以实现 fallback 模式。

利用 fallback 模式,当远程服务调用失败时,服务消费者不会产生异常,而是会执行执行 fallback 代码,尝试通过其他方式完成操作。

首先,需要在 properties 文件中添加 feign.hystrix.enabled=true 来启用 Hystrix,这样,就可以实现在服务失败时调用的 fallback 方法:

定义 fallback 实现:

@Component
public class JSONPlaceHolderFallback implements JSONPlaceHolderClient {

    @Override
    public List<Post> getPosts() {
        return Collections.emptyList();
    }

    @Override
    public Post getPostById(Long postId) {
        return null;
    }
}

然后,在 @FeignClient 注解中设置 fallback 类:

@FeignClient(value = "jplaceholder",
  url = "https://jsonplaceholder.typicode.com/",
  fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
    // API
}

7、日志

默认情况下,会为每个 Feign 客户端创建一个 logger。

要启用日志,应在 application.properties 文件中使用客户端接口的包名来声明日志级别:

logging.level.com.baeldung.cloud.openfeign.client: DEBUG

也可以通过完整的客户端接口类名来设置特定客户端的日志级别:

logging.level.com.baeldung.cloud.openfeign.client.JSONPlaceHolderClient: DEBUG

可以为每个客户端配置 Logger.Level Bean 来表示日志级别:

public class ClientConfiguration {
    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }
}

有四种日志级别可供选择:

  • NONE:不记录日志,这是默认设置
  • BASIC:只记录请求方法、URL 和响应状态码
  • HEADERS:记录基本信息以及请求和响应头
  • FULL:记录请求和响应的 Body、Header 和元数据

8、异常处理

Feign 默认的异常处理器 ErrorDecoder.default 总是抛出 FeignException

要自定义抛出的异常,可以实现 ErrorDecoder 接口来自定义异常处理器:

public class CustomErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {

        switch (response.status()){
            case 400:
                return new BadRequestException();
            case 404:
                return new NotFoundException();
            default:
                return new Exception("Generic error");
        }
    }
}

然后,就像之前那样,在 Configuration 类中添加一个 Bean 来替换默认的 ErrorDecoder

public class ClientConfiguration {

    @Bean
    public ErrorDecoder errorDecoder() {
        return new CustomErrorDecoder();
    }
}

9、总结

本文介绍了如何使用 Spring Cloud OpenFeign,介绍了如何进行自定义配置、配置拦截器、配置 Hystrix、配置日志、自定义异常处理等等。


Ref:https://www.baeldung.com/spring-cloud-openfeign