OpenFeign使用详解

news2024/9/28 3:57:57

什么是OpenFeign?

OpenFeign 是一个声明式的 HTTP 客户端,旨在简化微服务架构中不同服务之间的 HTTP 调用。它通过集成 Ribbon 实现了客户端负载均衡,并且能够与 Eureka、Consul 等服务发现组件无缝对接。使用 OpenFeign,开发者只需定义接口并使用注解来配置 HTTP 请求,从而避免了编写大量的模板代码。

cloud官网介绍Feign:Spring Cloud OpenFeign

OpenFeign源码:GitHub - OpenFeign/feign: Feign makes writing java http clients easier

Feign 的实现

Feign 在 Ribbon + RestTemplate 的基础上进行了进一步封装,帮助开发者定义和实现依赖服务的接口。通过 Feign,开发者只需创建一个接口并使用注解进行配置,即可完成对服务提供方的接口绑定,简化了使用 Spring Cloud Ribbon 时自动封装服务调用客户端的开发工作。

Feign 和 OpenFeign 的区别

特性FeignOpenFeign
依赖<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>    
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
作用轻量级 RESTful HTTP 服务客户端,内置 Ribbon 用于客户端负载均衡,调用服务注册中心的服务。在 Feign 的基础上支持 SpringMVC 的注解(如 @RequestMapping),通过动态代理生成实现类,实现负载均衡并调用其他服务。

OpenFeign 的优点

  1. 声明式调用:通过注解定义 HTTP 请求,代码更加简洁和易读。

  2. 集成 Ribbon:支持客户端负载均衡,确保请求在多个服务实例之间均匀分布。

  3. 支持服务发现:与 Eureka、Consul 等服务发现组件无缝集成,自动发现和调用服务。

  4. 支持熔断:与 Hystrix、Sentinel 等熔断器集成,提升系统的稳定性和容错能力。

  5. 可扩展性:支持自定义拦截器、编码器和解码器,便于根据需求进行扩展和定制化。

基本用法

父项目的 pom.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cloud</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging> <!-- 这里设置为pom -->
    <name>demo</name>
    <description>demo</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2023.0.2</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-dependencies -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2023.0.1.0</version>
            <type>pom</type>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <!-- 定义子模块 -->
    <modules>
        <module>service1</module>
        <module>service2</module>
        <module>common</module>
    </modules>

</project>

子项目 common

common 是一个简单的 Spring Boot 服务,提供一个 REST API。

ServiceBController.java

package com.example.common.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceBController {

    @GetMapping("/api/resource")
    public String getResource() {
        return "来自服务c的问候!";
    }
}

子项目 service-a

service1 使用 OpenFeign 调用 service-b 的服务。

Service1Application.java

在启动类,加上 @EnableFeignClients 注解。

package cloud.service1;

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

@SpringBootApplication
@EnableFeignClients
public class Service1Application {

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

}

ServiceBClient.java

package cloud.service1.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "service-b", url = "http://localhost:8080")
public interface ServiceBClient {

    @GetMapping("/api/resource")
    String getResource();
}

ServiceAController.java 

package cloud.service1.controller;


import cloud.service1.client.ServiceBClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServiceAController {

    private final ServiceBClient serviceBClient;

    public ServiceAController(ServiceBClient serviceBClient) {
        this.serviceBClient = serviceBClient;
    }

    @GetMapping("/api/resource")
    public String getResource() {
        return serviceBClient.getResource();
    }
}

如图所示,证明已经访问成功了。

@FeignClient 标签的常用属性 

@FeignClient 注解是 Spring Cloud OpenFeign 中用于声明一个 Feign 客户端的核心注解。它提供了丰富的属性来配置 Feign 客户端的行为,以满足不同场景下的微服务调用需求。

1、name

  • 类型: String
  • 描述: 指定 Feign 客户端调用的服务名称。这是一个必填属性,通常是注册在服务发现(如 Nacos)中的服务名。
@FeignClient(name = "demo-user")
public interface UserClient {
    // ...
}

2、url

  • 类型: String
  • 描述: 指定服务的 URL,通常用于调试或服务未注册到服务发现时。url 属性会覆盖 name 属性。
@FeignClient(name = "demo-user", url = "http://localhost:8080")
public interface UserClient {
    // ...
}

3、configuration

  • 类型: Class<?>[]
  • 描述: 指定自定义配置类,配置类可以用来定制 Feign 客户端的行为,如请求拦截器、编码器和解码器等。
@FeignClient(name = "demo-user", configuration = FeignConfig.class)
public interface UserClient {
    // ...
}

配置类示例:

@Configuration
public class FeignConfig {
    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> template.header("Custom-Header", "CustomHeaderValue");
    }
}

4、fallback

  • 类型: Class<?>
  • 描述: 指定服务降级的实现类。当 Feign 客户端调用失败时,会调用 fallback 指定的类中的方法。
@FeignClient(name = "demo-user", fallback = UserClientFallback.class)
public interface UserClient {
    // ...
}

5、fallbackFactory

  • 类型: Class<?>
  • 描述: 指定服务降级的工厂类,该工厂类可以提供更多的上下文信息,例如异常信息。
@FeignClient(name = "demo-user", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
    // ...
}

6、path

  • 类型: String
  • 描述: 指定服务的统一前缀路径,在定义 Feign 接口的方法时可以省略该路径。
@FeignClient(name = "demo-user", path = "/api/users")
public interface UserClient {
    @GetMapping("/{id}")
    User getUserById(@PathVariable("id") Long id);
}

7、decode404

  • 类型: boolean
  • 描述: 指定是否将 HTTP 404 响应解码为 Feign 客户端的 fallback,默认值为 false。
@FeignClient(name = "demo-user", decode404 = true)
public interface UserClient {
    // ...
}

8、primary

  • 类型: boolean
  • 描述: 指定该 Feign 客户端是否为主要的 @Primary Bean,这对某些场景下的自动装配很有用,默认值为 true。
@FeignClient(name = "demo-user", primary = false)
public interface UserClient {
    // ...
}

9、contextId

  • 类型: String
  • 描述: 用于在多 Feign 客户端实例中区分不同的上下文 ID。特别适用于多个 Feign 客户端指向同一服务时的配置。
@FeignClient(name = "demo-user", contextId = "userClient1")
public interface UserClient1 {
    // ...
}

@FeignClient(name = "demo-user", contextId = "userClient2")
public interface UserClient2 {
    // ...
}

添加请求头信息

在 Spring Cloud OpenFeign 中,可以通过多种方式添加请求头信息。以下是三种常见的方法:

1. 在方法参数上添加请求头信息

可以在 Feign 客户端接口的方法参数上使用 @RequestHeader 注解来添加请求头信息。

@FeignClient(name = "demo-user")
public interface UserClient {

    @GetMapping("/api/users/{id}")
    User getUserById(@PathVariable("id") Long id, @RequestHeader("Custom-Header") String customHeader);
}

2. 使用 Feign 配置类定义请求拦截器

如果需要在所有请求中添加相同的请求头,可以通过定义一个 Feign 请求拦截器来实现。

定义 Feign 配置类:

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header("Custom-Header", "CustomHeaderValue");
            requestTemplate.header("Another-Header", "AnotherHeaderValue");
        };
    }
}

在 Feign 客户端中使用配置类: 

@FeignClient(name = "demo-user", configuration = FeignConfig.class)
public interface UserClient {

    @GetMapping("/api/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

3. 动态添加请求头信息

如果需要根据某些条件动态添加请求头信息,可以在拦截器中编写逻辑。

定义 Feign 配置类:

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            // 动态添加请求头信息
            String customHeaderValue = getCustomHeaderValue();
            requestTemplate.header("Custom-Header", customHeaderValue);
        };
    }

    private String getCustomHeaderValue() {
        // 根据某些条件动态生成请求头值
        return "DynamicHeaderValue";
    }
}

在 Feign 客户端中使用配置类:

@FeignClient(name = "demo-user", configuration = FeignConfig.class)
public interface UserClient {

    @GetMapping("/api/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

超时控制

在 Spring Cloud OpenFeign 中,超时控制是非常重要的,特别是在微服务架构中,确保服务之间的调用不会因为超时而导致整个系统的不稳定。OpenFeign 提供了两种超时参数:connectTimeout 和 readTimeout,分别用于控制连接超时和读取超时。

第一步:提供方接口,制造超时场景

首先,我们需要在提供方接口中制造一个超时场景,以便在消费方调用时能够触发超时。

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) throws InterruptedException {
        // 模拟超时场景
        Thread.sleep(5000); // 休眠 5 秒
        return new User(id, "John Doe");
    }
}

第二步:消费方接口调用

在消费方接口中调用提供方接口,并配置超时参数。

@FeignClient(name = "demo-user", url = "http://localhost:8080")
public interface UserClient {

    @GetMapping("/api/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

第三步:超时处理

我们可以在默认客户端和命名客户端上配置超时。OpenFeign 使用两个超时参数:

  • connectTimeout:防止因服务器处理时间过长而阻塞调用者。
  • readTimeout:从连接建立时开始应用,当返回响应的时间过长时就会被触发。

配置超时参数

可以通过配置文件(如 application.yml 或 application.properties)来设置超时参数。

示例:application.yml

feign:
  client:
    config:
      default:
        connectTimeout: 2000  # 连接超时时间,单位为毫秒
        readTimeout: 3000     # 读取超时时间,单位为毫秒

示例:application.properties

feign.client.config.default.connectTimeout=2000
feign.client.config.default.readTimeout=3000

手动创建 Feign Client

在 Spring Cloud OpenFeign 中,通常使用 @FeignClient 注解来声明一个 Feign 客户端。然而,有时可能需要手动创建 Feign 客户端,例如在某些特殊场景下,或者需要更细粒度的控制。

示例代码

定义 Feign 接口

首先,定义一个标准的 Feign 接口。

public interface FooClient {

    @GetMapping("/api/foo")
    String getFoo();
}

手动创建 Feign 客户端

使用 Feign Builder API 手动创建两个 Feign 客户端实例,并为每个客户端配置不同的请求拦截器。

import feign.Feign;
import feign.auth.BasicAuthRequestInterceptor;
import feign.Client;
import feign.Contract;
import feign.Encoder;
import feign.Decoder;
import feign.micrometer.MicrometerObservationCapability;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.RestController;

// 导入 Spring Cloud OpenFeign 提供的默认配置
@Import(FeignClientsConfiguration.class)
@RestController
public class FooController {

    // 定义两个 Feign 客户端实例
    private FooClient fooClient;
    private FooClient adminClient;

    // 自动注入 Feign 所需的组件
    @Autowired
    public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerObservationCapability micrometerObservationCapability) {
        // 创建第一个 Feign 客户端实例,配置用户认证
        this.fooClient = Feign.builder()
                .client(client) // 设置客户端
                .encoder(encoder) // 设置编码器
                .decoder(decoder) // 设置解码器
                .contract(contract) // 设置契约(注解解析器)
                .addCapability(micrometerObservationCapability) // 添加 Micrometer 观测能力
                .requestInterceptor(new BasicAuthRequestInterceptor("user", "user")) // 设置请求拦截器,使用用户认证
                .target(FooClient.class, "https://PROD-SVC"); // 指定目标服务 URL

        // 创建第二个 Feign 客户端实例,配置管理员认证
        this.adminClient = Feign.builder()
                .client(client) // 设置客户端
                .encoder(encoder) // 设置编码器
                .decoder(decoder) // 设置解码器
                .contract(contract) // 设置契约(注解解析器)
                .addCapability(micrometerObservationCapability) // 添加 Micrometer 观测能力
                .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin")) // 设置请求拦截器,使用管理员认证
                .target(FooClient.class, "https://PROD-SVC"); // 指定目标服务 URL
    }

    // 示例方法,使用 fooClient 和 adminClient
    public void exampleMethod() {
        String fooResponse = fooClient.getFoo(); // 调用用户认证的 Feign 客户端
        String adminResponse = adminClient.getFoo(); // 调用管理员认证的 Feign 客户端
        System.out.println("Foo Response: " + fooResponse);
        System.out.println("Admin Response: " + adminResponse);
    }
}

Feign Spring Cloud CircuitBreaker 的支持

Spring Cloud OpenFeign 提供了对 Spring Cloud CircuitBreaker 的支持,使得在 Feign 客户端中可以轻松集成熔断器(Circuit Breaker)功能。通过配置,可以全局启用或禁用 CircuitBreaker 支持,并且可以自定义 CircuitBreaker 的名称模式。

启用 Spring Cloud CircuitBreaker 支持

如果 Spring Cloud CircuitBreaker 在 classpath 上,并且 spring.cloud.openfeign.circuitbreaker.enabled=true,Feign 将用 CircuitBreaker 来包装所有方法。

示例配置

spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true

禁用特定客户端的 CircuitBreaker 支持

为了在每个客户端的基础上禁用 Spring Cloud CircuitBreaker 的支持,可以创建一个具有 "prototype" scope 的 Feign.Builder。

import feign.Feign;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class FooConfiguration {

    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}

CircuitBreaker 名称模式

CircuitBreaker 的名称遵循这种模式 <feignClientClassName>#<calledMethod>(<parameterTypes>)。例如,当调用一个带有 FooClient 接口的 @FeignClient,并且被调用的接口方法 bar 没有参数,那么 CircuitBreaker 的名称将是 FooClient#bar()。

自定义 CircuitBreaker 名称模式

从 2020.0.2 开始,CircuitBreaker 名称模式已经从 <feignClientName>_<calledMethod> 改变。使用 2020.0.4 中引入的 CircuitBreakerNameResolver,可以保留旧的命名模式。

import org.springframework.cloud.client.circuitbreaker.CircuitBreakerNameResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;

@Configuration
public class FooConfiguration {

    @Bean
    public CircuitBreakerNameResolver circuitBreakerNameResolver() {
        return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
    }
}

启用 Spring Cloud CircuitBreaker Group

要启用 Spring Cloud CircuitBreaker group,请将spring.cloud.openfeign.circuitbreaker.group.enabled 属性设置为 true(默认为 false)。

spring:
  cloud:
    openfeign:
      circuitbreaker:
        group:
          enabled: true

使用配置属性配置 CircuitBreaker

在 Spring Cloud OpenFeign 中,可以通过配置属性来配置 CircuitBreaker。

定义 Feign 客户端

首先,定义一个 Feign 客户端接口。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

// 使用 @FeignClient 注解定义 Feign 客户端
@FeignClient(url = "http://localhost:8080")
public interface DemoClient {

    // 定义一个 GET 请求方法
    @GetMapping("/demo")
    String getDemo();
}

配置 CircuitBreaker

通过配置属性来配置 CircuitBreaker。

spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true  # 启用 CircuitBreaker
        alphanumeric-ids:
          enabled: true  # 启用字母数字 ID

resilience4j:
  circuitbreaker:
    instances:
      DemoClientgetDemo:  # CircuitBreaker 实例名称
        minimumNumberOfCalls: 69  # 最小调用次数
  timelimiter:
    instances:
      DemoClientgetDemo:  # 时间限制器实例名称
        timeoutDuration: 10s  # 超时时间

如果你想切换回 Spring Cloud 2022.0.0 之前使用的 CircuitBreaker 名称模式,可以将 spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled 设置为 false。

spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true
        alphanumeric-ids:
          enabled: false  # 禁用字母数字 ID,使用旧的命名模式

Feign Spring Cloud CircuitBreaker Fallback

Spring Cloud CircuitBreaker 支持 fallback 的概念:一个默认的代码路径,在 circuit 打开或出现错误时执行。要为一个给定的 @FeignClient 启用 fallback,可以将 fallback 属性设置为实现 fallback 的类名。我们还需要将我们的实现声明为一个 Spring Bean。

定义 Feign 客户端

定义一个 Feign 客户端接口,并使用 @FeignClient 注解,设置 fallback 属性。

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

@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    Hello getHello();

    @RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
    String getException();
}

定义 Fallback 类

定义一个 Fallback 类,实现 Feign 客户端接口,并在熔断器打开或出现错误时提供降级响应。

import org.springframework.stereotype.Component;
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;

@Component
static class Fallback implements TestClient {

    @Override
    public Hello getHello() {
        throw new NoFallbackAvailableException("Boom!", new RuntimeException());
    }

    @Override
    public String getException() {
        return "Fixed response";
    }
}

使用 FallbackFactory 访问触发原因

如果需要访问使 fallback 触发的原因,可以使用 @FeignClient 里面的 fallbackFactory 属性。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.stereotype.Component;
import org.springframework.cloud.client.circuitbreaker.FallbackFactory;

@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallbackFactory = TestClientFallbackFactory.class)
protected interface TestClient {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    Hello getHello();

    @RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
    String getException();
}

@Component
static class TestClientFallbackFactory implements FallbackFactory<TestClient> {

    @Override
    public TestClient create(Throwable cause) {
        return new TestClient() {
            @Override
            public Hello getHello() {
                System.out.println("Fallback cause: " + cause);
                throw new NoFallbackAvailableException("Boom!", cause);
            }

            @Override
            public String getException() {
                System.out.println("Fallback cause: " + cause);
                return "Fixed response";
            }
        };
    }
}

Feign 和 @Primary

当使用 Feign 与 Spring Cloud CircuitBreaker fallback 时,ApplicationContext 中可能存在多个相同类型的 Bean。这将导致 @Autowired 不起作用,因为没有确切的一个 Bean,或一个被标记为 @Primary 的 Bean。为了解决这个问题,Spring Cloud OpenFeign 将所有 Feign 实例标记为 @Primary,因此 Spring Framework 将知道要注入哪个 Bean。在某些情况下,这可能是不可取的。要关闭这种行为,将 @FeignClient 的 primary 属性设置为 false。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "test", url = "http://localhost:${server.port}/", primary = false)
public interface TestClient {

    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    String getHello();
}

Feign 继承的支持

Feign 支持接口继承,这意味着你可以定义一个通用的接口,然后在多个 Feign 客户端中继承和重用这个接口。通过接口继承,可以减少代码重复,提高代码的可维护性和可读性。

定义通用接口

首先,定义一个通用的接口,包含一些通用的方法。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

public interface CommonApi {

    @GetMapping("/common/{id}")
    String getCommon(@PathVariable("id") Long id);
}

定义 Feign 客户端接口

定义一个 Feign 客户端接口,继承通用接口,并添加一些特定于该客户端的方法。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "demo-service", url = "http://localhost:8080")
public interface DemoClient extends CommonApi {

    @GetMapping("/demo/{id}")
    String getDemo(@PathVariable("id") Long id);
}

使用 Feign 客户端

在需要使用 Feign 客户端的地方,注入并调用接口方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @Autowired
    private DemoClient demoClient;

    @GetMapping("/demo/{id}")
    public String getDemo(@PathVariable Long id) {
        return demoClient.getDemo(id);
    }

    @GetMapping("/common/{id}")
    public String getCommon(@PathVariable Long id) {
        return demoClient.getCommon(id);
    }
}

Feign request/response 压缩

在微服务架构中,网络传输的数据量可能会非常大,尤其是在处理大量数据或频繁调用远程服务时。为了减少网络传输的开销,可以对 Feign 的请求和响应进行压缩。Feign 支持通过配置启用 Gzip 压缩,从而减少数据传输的大小,提高性能。

在配置文件中启用 Feign 的请求和响应压缩。

application.yml

spring:
  cloud:
    openfeign:
      compression:
        request:
          enabled: true  # 启用请求压缩
          mime-types: text/xml,application/xml,application/json  # 指定压缩的 MIME 类型
          min-request-size: 2048  # 指定最小请求大小(字节)
        response:
          enabled: true  # 启用响应压缩

application.properties

spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json
spring.cloud.openfeign.compression.request.min-request-size=2048
spring.cloud.openfeign.compression.response.enabled=true

Feign 日志

每个创建的 Feign 客户端都会创建一个 logger。默认情况下,logger 的名字是用于创建 Feign 客户端的接口的全类名称。Feign 的日志只响应 DEBUG 级别。

配置 Feign 日志

1、配置日志级别

在 application.yml 或 application.properties 中配置 Feign 客户端的日志级别。

application.yml

logging:
  level:
    project.user.UserClient: DEBUG

application.properties

logging.level.project.user.UserClient=DEBUG

配置 Logger.Level

可以为每个客户端配置 Logger.Level 对象,告诉 Feign 要记录多少内容。选择是:

  • NONE: 没日志(默认)。
  • BASIC: 只记录请求方法和 URL 以及响应状态代码和执行时间。
  • HEADERS: 记录基本信息以及请求和响应头。
  • FULL: 记录请求和响应的 header、正文和元数据。
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

Micrometer 的支持

Spring Cloud OpenFeign 提供了对 Micrometer 的支持,使得 Feign 客户端的调用可以被 Micrometer 观察到。通过启用 Micrometer 支持,可以收集 Feign 客户端的调用指标,并将其集成到 Micrometer 的监控系统中。

配置 Micrometer 支持

在配置文件中启用 Micrometer 支持。

application.yml

spring:
  cloud:
    openfeign:
      micrometer:
        enabled: true  # 启用 Micrometer 支持

application.properties

spring.cloud.openfeign.micrometer.enabled=true

禁用 Micrometer 支持

可以通过以下两种方式禁用 Micrometer 支持:

  • 从 classpath 中排除 feign-micrometer。
  • 将 spring.cloud.openfeign.micrometer.enabled 设置为 false。

application.yml

spring:
  cloud:
    openfeign:
      micrometer:
        enabled: false  # 禁用 Micrometer 支持

application.properties

spring.cloud.openfeign.micrometer.enabled=false

自定义 MicrometerObservationCapability

可以通过注册你自己的 MicrometerObservationCapability Bean 来自定义 Micrometer 支持。

import io.micrometer.observation.ObservationRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cloud.openfeign.MicrometerObservationCapability;

@Configuration
public class FooConfiguration {

    @Bean
    public MicrometerObservationCapability micrometerObservationCapability(ObservationRegistry registry) {
        return new MicrometerObservationCapability(registry);
    }
}

使用 MicrometerCapability

仍然可以在 Feign 中使用 MicrometerCapability(仅支持指标),你需要禁用 Micrometer 支持(spring.cloud.openfeign.micrometer.enabled=false)并创建一个 MicrometerCapability Bean。

import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cloud.openfeign.MicrometerCapability;

@Configuration
public class FooConfiguration {

    @Bean
    public MicrometerCapability micrometerCapability(MeterRegistry meterRegistry) {
        return new MicrometerCapability(meterRegistry);
    }
}

Feign 缓存

Spring Cloud OpenFeign 提供了对 Spring 缓存的支持,使得 Feign 客户端可以识别其接口上的 @Cache* 注解。通过启用缓存支持,可以减少对远程服务的调用次数,提高性能。

启用 Feign 缓存

在 Spring Boot 应用的主类上启用缓存。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

定义一个 Feign 客户端接口,并使用 @Cacheable 注解。 

import org.springframework.cache.annotation.Cacheable;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "demo-service", url = "http://localhost:8080")
public interface DemoClient {

    @GetMapping("/demo/{filterParam}")
    @Cacheable(cacheNames = "demo-cache", key = "#keyParam")
    String demoEndpoint(String keyParam, @PathVariable String filterParam);
}

禁用 Feign 缓存

可以通过属性 spring.cloud.openfeign.cache.enabled=false 来禁用 Feign 缓存功能。

spring:
  cloud:
    openfeign:
      cache:
        enabled: false  # 禁用 Feign 缓存

Feign @QueryMap 的支持

Spring Cloud OpenFeign 提供了一个等价的 @SpringQueryMap 注解,用于将 POJO 或 Map 参数注解为查询参数 map。通过使用 @SpringQueryMap 注解,可以更方便地将复杂对象转换为查询参数,而不需要手动拼接查询字符串。

1. 定义 POJO 类

首先,定义一个 POJO 类,用于表示查询参数。

public class QueryParams {
    private String param1;
    private String param2;

    // Getters and Setters
}

2. 定义 Feign 客户端接口

定义一个 Feign 客户端接口,并使用 @SpringQueryMap 注解将 POJO 或 Map 参数注解为查询参数 map。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "demo-service", url = "http://localhost:8080")
public interface DemoClient {

    @GetMapping("/demo")
    String getDemo(@SpringQueryMap QueryParams queryParams);
}

3. 使用 Feign 客户端

在需要使用 Feign 客户端的地方,注入并调用接口方法。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    @Autowired
    private DemoClient demoClient;

    @GetMapping("/demo")
    public String getDemo() {
        QueryParams queryParams = new QueryParams();
        queryParams.setParam1("value1");
        queryParams.setParam2("value2");
        return demoClient.getDemo(queryParams);
    }
}

HATEOAS 的支持

Spring 提供了一些 API 来创建遵循 HATEOAS 原则的 REST 表示,如 Spring Hateoas 和 Spring Data REST。如果你的项目使用了 org.springframework.boot:spring-boot-starter-hateoas 或 org.springframework.boot:spring-boot-starter-data-rest starter,Feign HATEOAS 支持会被默认启用。

当HATEOAS支持被启用时,Feign 客户端被允许序列化和反序列化 HATEOAS 表示模型: EntityModel、 CollectionModel 和 PagedModel.。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "demo-service", url = "http://localhost:8080")
public interface DemoClient {

    @GetMapping("/demo/{id}")
    EntityModel<Demo> getDemo(@PathVariable("id") Long id);

    @GetMapping("/demos")
    CollectionModel<EntityModel<Demo>> getAllDemos();
}

OAuth2 的支持

Spring Cloud OpenFeign 提供了对 OAuth2 的支持,使得 Feign 客户端可以自动获取并附加 OAuth2 访问令牌到请求头中。通过启用 OAuth2 支持,可以简化与 OAuth2 保护的资源的交互。

 在配置文件中启用 OAuth2 支持,并指定 OAuth2 客户端的注册 ID。

spring:
  cloud:
    openfeign:
      oauth2:
        enabled: true  # 启用 OAuth2 支持
        clientRegistrationId: my-client-registration-id  # 指定 OAuth2 客户端的注册 ID

转换负载均衡的 HTTP 请求

在 Spring Cloud OpenFeign 中,你可以使用选定的 ServiceInstance 来转换负载均衡的 HTTP 请求。为了实现这一点,你需要实现和定义 LoadBalancerFeignRequestTransformer,该接口允许你在请求发送到目标服务之前对其进行转换。

实现 LoadBalancerFeignRequestTransformer

1. 实现 LoadBalancerFeignRequestTransformer

首先,实现 LoadBalancerFeignRequestTransformer 接口,并在 transformRequest 方法中定义请求转换逻辑。

 

import feign.Request;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerFeignRequestTransformer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class FeignConfig {

    // 定义一个 LoadBalancerFeignRequestTransformer Bean
    @Bean
    public LoadBalancerFeignRequestTransformer transformer() {
        return new LoadBalancerFeignRequestTransformer() {

            // 实现 transformRequest 方法,定义请求转换逻辑
            @Override
            public Request transformRequest(Request request, ServiceInstance instance) {
                // 创建一个新的请求头 Map,并复制原始请求的请求头
                Map<String, Collection<String>> headers = new HashMap<>(request.headers());
                
                // 添加自定义请求头,包含服务 ID
                headers.put("X-ServiceId", Collections.singletonList(instance.getServiceId()));
                
                // 添加自定义请求头,包含实例 ID
                headers.put("X-InstanceId", Collections.singletonList(instance.getInstanceId()));
                
                // 创建并返回一个新的 Request 对象,包含转换后的请求头
                return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(),
                        request.requestTemplate());
            }
        };
    }
}

2. 配置 Feign 客户端

在 Feign 客户端配置中,启用负载均衡并使用自定义的 LoadBalancerFeignRequestTransformer。 

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

// 定义一个 Feign 客户端接口,启用负载均衡
@FeignClient(name = "demo-service")
public interface DemoClient {

    // 定义一个 GET 请求方法
    @GetMapping("/demo/{id}")
    String getDemo(@PathVariable("id") Long id);
}

参考文章

常见的应用属性

Spring Cloud OpenFeign 中文文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2167571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用ssm实现基于java web的防疫工作志愿者服务平台的设计与实现

TOC ssm693基于java web的防疫工作志愿者服务平台的设计与实现jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进…

Focalboard开源项目管理系统本地Windows部署与远程访问协同办公

文章目录 前言1. 使用Docker本地部署Focalboard1.1 在Windows中安装 Docker1.2 使用Docker部署Focalboard 2. 安装Cpolar内网穿透工具3. 实现公网访问Focalboard4. 固定Focalboard公网地址 &#x1f4a1; 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&am…

Redis 篇-深入了解在 Linux 的 Redis 网络模型结构及其流程(阻塞 IO、非阻塞 IO、IO 多路复用、异步 IO、信号驱动 IO)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 用户空间与内核空间概述 2.0 Redis 网络模型 2.1 Redis 网络模型 - 阻塞 IO 2.2 Redis 网络模型 - 非阻塞 IO 2.3 Redis 网络模型 - IO 多路复用 2.3.1 IO 多路复…

vscode【实用插件】Markdown Preview Enhanced 预览 .md 文件

安装 在 vscode 插件市场的搜索 Markdown Preview Enhanced点安装 使用 用 vscode 打开任意 .md 文件右键快捷菜单 最终效果 可打开导航目录

liunxcentos7下 跟目录空间不足docker load镜像报错空间不足

前两天在公司&#xff0c;做jenkins流水线项目&#xff0c;然后把项目放到docker容器里面运行&#xff0c;就在我把镜像打好包的时候正准备往服务器里面导入镜像的时候报错&#xff1a;如图所示 这时发现自己的根目录空间不足。 解决办法&#xff1a;重新加一块磁盘将磁盘挂载…

Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动

文章目录 1、 buildroot源码获取2、buildroot配置3、buildroot编译4、挂载根文件系统 在上一篇 Qemu开发ARM篇-4、kernel交叉编译运行演示中&#xff0c;我们编译了kernel&#xff0c;并在qemu上进行了运行&#xff0c;但到最后&#xff0c;在挂载根文件系统时候&#xff0c;挂…

基于 Redis 实现滑动窗口的限流

⏳ 限流场景&#xff1a;突发流量&#xff0c;恶意流量&#xff0c;业务本身需要 基于 Redis 实现滑动窗口的限流是一种常见且高效的做法。Redis 是一种内存数据库&#xff0c;具有高性能和支持原子操作的特点&#xff0c;非常适合用来实现限流功能。下面是一个使用 Redis 实现…

谷歌浏览器如何把常用的网址创建快捷方式到电脑桌面?

1、打开想要创建快捷方式的网页之后&#xff0c;点击谷歌浏览器右上角的【三个点】 2、选择【保存并分享】&#xff0c;再选择【创建快捷方式】 3、之后在浏览器上方弹出的框中&#xff0c;重新命名快捷方式。 然后&#xff0c;点击【创建】 4、之后&#xff0c;即可在电…

手机解压软件加密指南:让文件更安全

在数字化时代&#xff0c;文件加密对于保护个人隐私和敏感信息的重要性不言而喻。随着互联网的飞速发展&#xff0c;我们的生活和工作越来越依赖于数字设备和网络。 然而&#xff0c;这也带来了一系列的安全风险&#xff0c;如黑客攻击、数据泄露等。文件加密技术成为了保护我…

mac m1 electron生产环境使用prisma,sqlite

最近在用electron开发一个适合自己的小应用&#xff0c;技术选型中使用prisma和sqlite在进行数据存储&#xff0c;写这篇文章的目的就是用来记录下遇到的一些问题。 开发环境使用prisma 1、开发环境使用prisma非常的简单&#xff0c;只需要按照教程安装prisma&#xff0c;然后…

vue嵌套路由刷新页面空白问题

问题描述 在vue项目开发中遇到这样一个问题&#xff0c;在history模式下通过页面点击路由跳转可以打开页面&#xff0c;但是在当前页面刷新就空白了&#xff0c;如下&#xff1a; 点击路由跳转页面是有的 刷新页面就空白 代码 {path: "/home",name: "home&qu…

TreeMap源码详解

优质博文&#xff1a;IT-BLOG-CN 背景&#xff1a;昨天有人问我&#xff0c;他想将Map中的Key按照顺序进行遍历&#xff0c;我说直接使用keySet方法获取到Set集合&#xff0c;因为它是集成Collection接口&#xff0c;所以包含了sort方法后遍历取value值即可。但当看到TreeMap的…

差旅报销的数智化转型 以分贝通为例

企业差旅报销的数智化转型之所以势在必行&#xff0c;源于传统差旅报销方式在效率、合规性和成本控制等方面存在严重不足。作为服务企业的一体化差旅报销管理平台&#xff0c;分贝通结合数千家合作伙伴的实际案例为企业提供定制化的差旅报销数智化解决方案&#xff0c;帮助企业…

【Python报错已解决】AttributeError: ‘tuple‘ object has no attribute ‘log_softmax‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

CentOS 7 中安装 docker 环境

作者&#xff1a;程序那点事儿 日期&#xff1a;2023/02/15 02:31 官网地址 官网文档 docker三种网络模式 Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0c; CentOS 7 满足最低内核的要求。 Docker 分为 CE 和 EE 两大版本。CE 即社区…

前端开发必备:实用Tool封装工具类方法大全

程序员必备宝典网站https://tmxkj.top/#/ 1.判断空值 /*** 判断是否是空值*/ export function isEmpty(value) {if (typeof value string) {return value.length 0 || value "";} else if (typeof value number) {return value 0;} else if (Array.isArray(va…

TypeScript 设计模式之【抽象工厂模式】

文章目录 抽象工厂模式:一个神奇的玩具制造商主要思想:玩具制造商的秘密这个制造商有什么好处和坏处?如何打造这个神奇的玩具制造商?代码实现案例抽象工厂模式主要优点抽象工厂模式主要缺点抽象工厂模式适用场景总结 抽象工厂模式:一个神奇的玩具制造商 想象一下&#xff0c…

(done) 声音信号处理基础知识(7) (Understanding Time Domain Audio Features)

参考&#xff1a;https://www.youtube.com/watch?vSRrQ_v-OOSg&t1s 时域特征包括&#xff1a; 1.幅度包络 2.均方根能量 3.过零率 振幅包络的定义&#xff1a;一个 frame 里&#xff0c;所有采样点中最大的振幅值 一个形象的关于振幅包络的可视化解释如下&#xff1a;…

基于SpringBoot + Vue的大学生日常消费管理系统设计与实现

文章目录 前言一、详细操作演示视频二、具体实现截图三、技术栈1.前端-Vue.js2.后端-SpringBoot3.数据库-MySQL4.系统架构-B/S 四、系统测试1.系统测试概述2.系统功能测试3.系统测试结论 五、项目代码参考六、数据库代码参考七、项目论文示例结语 前言 &#x1f49b;博主介绍&a…

fiddler抓包07_抓IOS手机请求

课程大纲 前提&#xff1a;电脑和手机连接同一个局域网 &#xff08;土小帽电脑和手机都连了自己的无线网“tuxiaomao”。&#xff09; 原理如下&#xff1a; 电脑浏览器抓包时&#xff0c;直接就是本机网络。手机想被电脑Fiddler抓包&#xff0c;就要把Fiddler变成手机和网络…