当前位置:首页 > 技术分析 > 正文内容

Open Feign 使用、原理、源码(openfeign feign)

ruisui883个月前 (02-03)技术分析19

OpenFegin @FeignClient注解详解

@FeignClient 注解具有以下可配置属性:

  • name:用于指定 Feign Client 的名称,同时也是 Spring Bean 的名称。默认情况下,使用注解的接口的简单类名作为名称。
  • url:用于指定 Feign Client 请求的 URL 地址。如果指定了该属性,则 Feign 将不会使用 Eureka 进行服务发现。
  • path:用于指定 Feign Client 调用的 URL 相对路径,默认为空。
  • fallback:用于指定 Fallback 类的 Class 对象。该类需要实现当前注解标注的接口,并提供相应的容错逻辑。
  • fallbackFactory:用于指定 Fallback 工厂类的 Class 对象。该工厂类需要实现 FallbackFactory 接口,并提供对应的容错逻辑。在创建 Fallback 实例时,会优先使用该工厂类。
  • decode404:指示是否将 404 响应视为正常响应。默认情况下,Feign 客户端只有在收到非 404 响应时才会将响应体解码为 Java 对象。
  • configuration:用于指定自定义的 Feign 配置类。这个自定义配置类必须是一个实现 FeignClientConfigurer 接口的 Bean。
  • primary:用于指定 Feign Client 是否为首选 Bean。如果有多个 Feign Client 需要注入,且没有指定首选 Bean,那么 Spring 容器会抛出异常。
  • qualifier:用于指定 Feign Client 的限定符,同样用于解决多个 Feign Client 需要注入的情况。
  • contextId:该属性用于在同一应用程序中创建不同的 Feign Client 实例。例如,如果您需要为两个不同的服务使用相同的 URL,则可以通过将 contextId 属性设置为不同的值来实现此目的。

Open Feign 使用示例

OpenFeign是一个基于Java的HTTP客户端,它的设计目标是使得编写Http客户端变得更加简单。OpenFeign提供了类似于Spring MVC注解风格的API,并且能够与Ribbon和Eureka等组件集成,从而实现负载均衡以及服务发现等功能。

下面是一个简单的使用OpenFeign进行HTTP请求的示例:

首先,在Maven中添加OpenFeign的依赖:


  org.springframework.cloud
  spring-cloud-starter-openfeign

然后,定义一个Feign客户端接口:

@FeignClient(name = "github", url = "https://api.github.com")
public interface GithubClient {
    @GetMapping("/users/{username}")
    User getUser(@PathVariable("username") String username);
}

其中,@FeignClient注解用于指定该接口是一个Feign客户端,name属性用于指定客户端名称,url属性用于指定请求的URL。

接着,定义一个POJO类User:

public class User {
    private String name;
    private String blog;
    // getters and setters
}

最后,在Spring Boot应用程序中使用该Feign客户端:

@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Autowired
    private GithubClient githubClient;
    
    @GetMapping("/users/{username}")
    public User getUser(@PathVariable String username) {
        return githubClient.getUser(username);
    }
}

@EnableFeignClients注解用于启用Feign客户端功能,@Autowired注解用于注入GithubClient接口的实例。

现在,我们就可以通过访问
http://localhost:8080/users/octocat来获取octocat用户的信息了。

Open Feign 原理

OpenFeign是一种基于接口的声明式Web服务客户端,它使得编写Web服务客户端变得更加简单。通过定义一个Java接口并使用注解来配置它,OpenFeign将自动地生成一个实现该接口的Web服务客户端。当我们调用该客户端时,它会发送HTTP请求到相应的Web服务端点,并将响应转换为我们所期望的类型。

OpenFeign的实现基于Netflix的Feign库。它支持多种HTTP请求方式和序列化协议,包括JSON和XML。在底层,OpenFeign使用了Ribbon和Hystrix来提供负载均衡和容错能力。

OpenFeign的工作原理可以大致分为以下几个步骤:

  1. 定义接口:首先需要定义一个 Java 接口,该接口中定义了需要访问的 REST API 方法,包括请求方式、请求路径、请求参数、请求头等信息。
  2. 注解配置:在接口方法上使用注解进行配置,包括请求方式、请求路径、请求参数、请求头等信息。OpenFeign 提供了很多注解,例如 @RequestMapping、@RequestParam、@RequestHeader 等。
  3. 生成代理类:在应用启动时,OpenFeign 会根据接口定义和注解配置生成一个代理类。这个代理类会通过 Java 反射机制调用底层的 HTTP 客户端进行请求。
  4. 发起请求:当调用接口方法时,实际上是调用了代理类的对应方法。代理类会根据注解配置生成 HTTP 请求,并将请求发送给服务端。
  5. 接收响应:服务端接收到请求后会返回一个 HTTP 响应。代理类会将响应解析成 Java 对象,并将其返回给调用方。

总之,OpenFeign通过在运行时生成Web服务客户端代码的方式,简化了Web服务客户端的开发。

Open Feign 自定义拦截器

请求拦截

OpenFeign 提供了 RequestInterceptor 接口,用于对 Feign 请求进行拦截和处理。通过实现该接口,可以在发送 Feign 请求前后进行一些自定义操作,例如添加公共请求头、签名等。

具体来说,RequestInterceptor 接口定义了两个方法:

  1. apply(RequestTemplate template):该方法会在发送 Feign 请求前被调用,传入的参数 template 是 HTTP 请求的模板,可以通过该模板设置请求头、请求体等信息。
  2. toString():该方法返回拦截器的名称,方便调试和排查问题。

下面是一个简单的示例,演示如何在 Feign 请求中添加公共请求头:

public class CustomHeaderInterceptor implements RequestInterceptor {

    private final String headerName;
    private final String headerValue;

    public CustomHeaderInterceptor(String headerName, String headerValue) {
        this.headerName = headerName;
        this.headerValue = headerValue;
    }

    @Override
    public void apply(RequestTemplate template) {
        template.header(headerName, headerValue);
    }

    @Override
    public String toString() {
        return "CustomHeaderInterceptor{" +
                "headerName='" + headerName + '\'' +
                ", headerValue='" + headerValue + '\'' +
                '}';
    }
}

在上述代码中,我们自定义了一个 CustomHeaderInterceptor 拦截器,它会在每次 Feign 请求中添加一个指定的请求头。使用该拦截器只需要在创建 Feign 客户端时将其加入即可:

MyClient client = Feign.builder()
        .requestInterceptor(new CustomHeaderInterceptor("Authorization", "Bearer token"))
        .target(MyClient.class, "http://localhost:8080");

这样,在每次调用 MyClient 接口的方法时,都会自动添加一个名为 "Authorization",值为 "Bearer token" 的请求头。

或者将CustomHeaderInterceptor注入到spring容器中。

响应拦截

在使用OpenFeign时,我们可以通过实现ResponseInterceptor接口来拦截响应。这个接口定义了一个方法void apply(Response response),该方法将在每次响应到达时被调用。

我们可以在这个方法中对响应进行处理,比如获取响应头、状态码、响应体等信息,并根据这些信息做出相应的处理。以下是一个简单的示例:

public class MyResponseInterceptor implements ResponseInterceptor {
 
    @Override
    public void apply(Response response) {
        // 获取响应体
        String responseBody = response.body().toString();
        System.out.println("Response Body: " + responseBody);
 
        // 获取响应状态码
        int status = response.status();
        System.out.println("Response Status: " + status);
 
        // 获取响应头信息
        Map> headers = response.headers();
        for (Map.Entry> header : headers.entrySet()) {
            System.out.println(header.getKey() + ": " + header.getValue());
        }
    }
}

在上面的代码中,我们实现了ResponseInterceptor接口,并重写了其中的apply()方法。在这个方法中,我们获取了响应体、状态码和响应头信息,并将其打印出来。当我们使用OpenFeign发起请求时,这个拦截器就会自动应用于响应中。

要使用自定义的响应拦截器,可以在使用@FeignClient注解的接口中指定configuration属性,例如:

@FeignClient(name = "example", configuration = MyFeignConfiguration.class)
public interface MyFeignClient {
    // ...
}

MyFeignConfiguration中可以注册自定义的响应拦截器,例如:

@Configuration
public class MyFeignConfiguration {
 
    @Bean
    public ResponseInterceptor myResponseInterceptor() {
        return new MyResponseInterceptor();
    }
}

这样,在使用MyFeignClient发起请求时,就会自动应用我们定义的响应拦截器了。

Open Feign 实现负载均衡策略

负载均衡策略本质:从注册中心获取对应的服务列表,根据服务的相关信息以及属性,选择一个服务进行请求。对标到框架上就是从OpenFeign中得到服务名称,Ribbon根据服务名称找到服务列表,并且根据策略选出服务,是由RestTemplate发送请求。

OpenFeign 实现负载均衡策略可以通过以下几种方式:

1.使用 Ribbon 进行负载均衡:可以通过在 Feign 配置类中配置 RibbonClient 对象,以便在远程调用时使用 Ribbon 进行负载均衡。同时需要在 Ribbon 配置类中设置负载均衡规则。

2.使用 Spring Cloud LoadBalancer 进行负载均衡:可以在 Feign 配置类中配置 LoadBalancerFeignClient 对象,以便在远程调用时使用 Spring Cloud LoadBalancer 进行负载均衡。

3.自定义负载均衡逻辑:可以通过实现 Feign 的 LoadBalancer 接口自定义负载均衡逻辑,并在 Feign 配置类中将该负载均衡器对象注入到 Feign.Builder 中。

示例代码如下:

// 使用 Ribbon 进行负载均衡
@FeignClient(name = "service-provider", configuration = RibbonConfig.class)
public interface ProviderService {
    // ...
}

@Configuration
public class RibbonConfig {

    @Bean
    public IRule ribbonRule() {
        return new RandomRule();
    }

    @Bean
    public ServerList ribbonServerList(IClientConfig config) {
        ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
        serverList.initWithNiwsConfig(config);
        return serverList;
    }

    @Bean
    public RibbonClient ribbonClient(IClientConfig config, ServerList serverList) {
        return RibbonClient.builder()
                .withServerList(serverList)
                .withLoadBalancerRetryHandler(new DefaultLoadBalancerRetryHandler(1, 1, true))
                .withClientConfig(config)
                .build();
    }
}

// 使用 Spring Cloud LoadBalancer 进行负载均衡
@FeignClient(name = "service-provider", configuration = LoadBalancerConfig.class)
public interface ProviderService {
    // ...
}

@Configuration
public class LoadBalancerConfig {

    @Bean
    public ServiceInstanceListSupplier serviceInstanceListSupplier(ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                .withDiscoveryClient()
                .withApplicationContext(context)
                .build();
    }

    @Bean
    public LoadBalancerFeignClient loadBalancerFeignClient(
            WebClient.Builder webClientBuilder, ReactiveLoadBalancer.Factory loadBalancerFactory) {
        return new LoadBalancerFeignClient(new ReactiveLoadBalancerFeignClient(webClientBuilder, loadBalancerFactory));
    }
}

// 自定义负载均衡逻辑
@FeignClient(name = "service-provider", configuration = MyLoadBalancerConfig.class)
public interface ProviderService {
    // ...
}

@Configuration
public class MyLoadBalancerConfig {

    @Bean
    public LoadBalancer myLoadBalancer() {
        return new MyLoadBalancer();
    }

    @Bean
    public Feign.Builder feignBuilder(LoadBalancer loadBalancer) {
        return Feign.builder().client(new CustomLoadBalancerFeignClient(loadBalancer));
    }
}

在上述示例代码中,分别使用了 Ribbon、Spring Cloud LoadBalancer 和自定义负载均衡逻辑三种方式实现了 OpenFeign 的负载均衡策略。其中 Ribbon 和 Spring Cloud LoadBalancer 的配置需要分别在 RibbonConfig 和 LoadBalancerConfig 中进行。而自定义的负载均衡逻辑则需要实现 LoadBalancer 接口,并在 MyLoadBalancerConfig 中将其注入到 Feign.Builder 对象中。

OpenFegin 自定义负载均衡策略、服务检查、获取服务、客户端

OpenFeign 实现负载均衡策略可以通过配置 Ribbon 的相关组件来实现。下面是一些常用的方法:

1.配置 IRule 接口实现类

IRule 是 Ribbon 提供的负载均衡策略接口,可以自定义实现类来定制负载均衡算法。OpenFeign 中可以通过以下方式进行配置:

@Configuration
public class FeignConfiguration {
    @Bean
    public IRule ribbonRule() {
        return new RandomRule(); // 配置为随机策略
    }
}

上面的示例中使用的是随机策略,还可以使用其他策略如轮询、最小连接数等等。

  1. 配置 IPing 接口实现类

IPing 接口定义了检查服务是否可用的方法,通过配置 IPing 的实现类可以定制服务检查策略。OpenFeign 中可以通过以下方式进行配置:

@Configuration
public class FeignConfiguration {
    @Bean
    public IPing ribbonPing() {
        return new PingUrl(); // 配置为 URL 检查策略
    }
}

上面的示例中使用的是 URL 检查策略,还可以使用其他策略如 ICMP Ping 等等。

  1. 配置 ServerList 接口实现类

ServerList 接口用于获取服务实例列表。默认情况下,Ribbon 使用 Eureka 或 Consul 等服务注册中心来获取服务实例列表,但也可以自定义实现类来获取服务实例列表。OpenFeign 中可以通过以下方式进行配置:

@Configuration
public class FeignConfiguration {
    @Bean
    public ServerList ribbonServerList(IClientConfig config) {
        return new ConfigurationBasedServerList(); // 配置为配置文件列表策略
    }
}

上面的示例中使用的是配置文件列表策略,还可以使用其他策略如 DNS 解析等等。

  1. 配置 IClientConfig 对象

IClientConfig 用于传递 Ribbon 配置参数,可以通过以下方式进行配置:

@Configuration
public class FeignConfiguration {
    @Bean
    public IClientConfig ribbonClientConfig() {
        return new DefaultClientConfigImpl();
    }
}

上面的示例中使用的是默认实现类 DefaultClientConfigImpl,还可以自定义实现类来定制配置参数。

综上所述,通过配置以上组件即可实现 OpenFeign 的负载均衡策略,具体使用时需要根据实际情况进行配置调整。

OpenFeign 源码解析

OpenFeign 是一个基于 Netflix Feign 的 Java 库,它提供了简单易用的 HTTP 请求接口,并且集成了 Ribbon 负载均衡组件。下面是 OpenFeign 的源码解析:

1.Feign.builder() 方法

该方法是 OpenFeign 中创建 Feign 客户端实例的入口,返回一个 Feign.Builder 对象,用于设置 Feign 客户端的参数和配置选项。

  1. FeignClientFactoryBean

该类是 Spring Boot 中创建 Feign 客户端的工厂类,负责生成 Feign 客户端的代理对象。其中,getObject() 方法会根据配置信息、注解信息等来创建 Feign 客户端实例。

  1. ReflectiveFeign

该类是 OpenFeign 中使用反射机制调用 Feign 客户端方法的核心类,定义了一系列静态方法用于构建和执行 HTTP 请求。其中,newInstance() 方法会使用 JDK 动态代理机制来创建 Feign 客户端的代理实例。

  1. feign.Contract 接口

该接口定义了 Feign 客户端接口和 HTTP 请求之间的映射规则,包括 URL、HTTP 方法、请求头、请求体等。默认情况下,OpenFeign 使用的是 Spring Cloud 中的 SpringMvcContract 实现类。

  1. feign.Logger 接口

该接口定义了 Feign 客户端的日志输出格式和级别,包括 NONE、BASIC、HEADERS 和 FULL 等级别。默认情况下,OpenFeign 使用的是 SLF4J 日志框架输出日志。

  1. feign.Client 接口

该接口定义了 Feign 客户端发送 HTTP 请求和接收响应的行为,并提供了多个实现类,包括 Apache HttpClient 和 OkHttp 实现类等。

  1. feign.codec.Encoder 接口和 feign.codec.Decoder 接口

这两个接口分别定义了请求消息体和响应消息体的编码和解码逻辑,根据要求可以使用不同的实现类来支持不同的编解码格式,如 JSON、XML、Protobuf 等。

  1. feign.codec.ErrorDecoder 接口

该接口定义了在 Feign 客户端请求失败时,如何将服务端返回的错误信息转换成异常信息并抛出,以及如何处理超时、连接异常等问题。

  1. feign.Contract.Default()

该类是 Contract 接口的默认实现类,它将请求方法与 HTTP 请求之间的映射规则定义为注解方式,如 @RequestMapping@GetMapping@PostMapping 等。

  1. feign.RequestTemplate

该类是 OpenFeign 中表示 HTTP 请求的模板类,用于存储请求 URL、HTTP 方法、请求头、请求体等信息,并提供了一系列方法用于设置和获取请求参数、请求头等。


源码

以下是 Feign 类的源码:

public class Feign {

  // 创建一个 Feign 客户端实例的入口,返回一个 Feign.Builder 对象
  public static Builder builder() {
    return new Builder();
  }

  // 使用指定的 InvocationHandler 和接口类型创建代理对象实例
  public static  T newInstance(Target target) {
    return newInstance(target, new HardCodedTarget<>(target.type(), target.name()));
  }

  // 使用指定的 InvocationHandler 和接口类型创建代理对象实例,并绑定到指定的 URL 上
  public static  T newInstance(Target target, InvocationHandler handler) {
    return (T) Proxy.newProxyInstance(target.type().getClassLoader(),
                                      new Class[] { target.type() },
                                      handler);
  }

  // 定义 Feign 客户端的构建器类,用于设置其参数和配置选项
  public static final class Builder extends Feign.Builder {
    // ...
  }
}

重要说明:newInstance() 方法使用了 JDK 的动态代理机制来创建代理对象,并且可以绑定到指定的 InvocationHandler 处理器上。其中,Target 接口表示请求目标信息,比如服务名、IP 地址、端口号等;HardCodedTarget 类则是对 Target 接口的默认实现。

public interface Target {
  Class type();
  String name();
  String url();
}

public final class HardCodedTarget implements Target {

  private final Class type;
  private final String name;

  public HardCodedTarget(Class type, String name) {
    this.type = checkNotNull(type, "type");
    this.name = checkNotNull(name, "name");
  }

  @Override
  public Class type() {
    return type;
  }

  @Override
  public String name() {
    return name;
  }

  @Override
  public String url() {
    throw new UnsupportedOperationException();
  }
}

重要说明:HardCodedTarget 类是 Target 接口的默认实现,它包含了请求目标的类型和名称等信息。

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map dispatch);

  default InvocationHandler wrap(InvocationHandler handler) {
    return handler;
  }
}

public abstract class InvocationHandler implements java.lang.reflect.InvocationHandler {

  // 调用 API 前预处理方法
  protected void preprocess(Target target, Method method, Object[] args) throws Throwable {}

  // 调用 API 后后处理方法
  protected void postprocess(Target target, Method method, Object[] args, Object result,
                             Throwable throwable) throws Throwable {}
}

重要说明:InvocationHandler 抽象类定义了调用 API 前后的预处理和后处理方法,可以在这些方法中执行一些额外的业务逻辑操作。

public interface Target {
  // ...
  Request apply(RequestTemplate input);
}

public interface RequestTemplate {

  void method(String method);

  void append(String key, String value);

  void header(String key, String value);

  void body(byte[] data, Charset charset);

  // ...
}

重要说明:Target 接口中的 apply() 方法用于将请求模板应用到 Feign 请求中,而 RequestTemplate 接口则用于设置请求模板的各种参数和选项。

public interface Encoder {

  // 将请求对象编码成指定格式的字节数组
  void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}

public interface Decoder {

  // 将响应字节数组解码成指定类型的对象
  Object decode(Response response, Type type) throws IOException, DecodeException;
}

重要说明:EncoderDecoder 接口分别定义了请求消息体和响应消息体的编码和解码逻辑,可以使用不同的实现类来支持不同的编解码格式,如 JSON、XML、Protobuf 等。

public interface Client {

  // 发送 HTTP 请求并获取响应
  Response execute(Request request, Request.Options options) throws IOException;
}

public interface ErrorDecoder {

  // 将服务端返回的错误信息转换成异常信息并抛出
  Exception decode(String methodKey, Response response);
}

public interface Logger {

  // 日志级别:NONE、BASIC、HEADERS 和 FULL 等级别
  enum Level {
    NONE, // 不输出日志
    BASIC, // 只输出请求方法和 URL
    HEADERS, // 输出请求和响应的头部信息
    FULL // 输出完整的请求和响应信息
  }

  // 输出日志信息
  void log(String configKey, String format, Object... args);
}

重要说明:Client 接口定义了发送 HTTP 请求的行为,并提供了多个实现类,包括 Apache HttpClient 和 OkHttp 实现类等;ErrorDecoder 接口定义了将服务端返回的错误信息转换成异常信息并抛出的逻辑,通常需要自定义实现类来处理自己的业务逻辑;Logger 接口定义了 Feign 客户端的日志输出格式和级别,可以使用不同实现类来支持不同的日志框架。

public interface Contract {

  List parseAndValidatateMetadata(Class targetType);

  // 构建 HTTP 请求模板
  MethodMetadata parseAndValidateMetadata(Method method);

  class Default extends AbstractContract {
    // ...
  }
}

重要说明:Contract 接口定义了 Feign 客户端接口和 HTTP 请求之间的映射规则,包括 URL、HTTP 方法、请求头、请求体等。默认情况下,OpenFeign 使用的是 Spring Cloud 中的 SpringMvcContract 实现类。AbstractContract 类提供了一些公共的解析逻辑和模板构建方法,而 Default 内部类则是 AbstractContract 的默认实现。

public abstract class AbstractContract implements Contract {

  protected interface AnnotatedParamMetadata {
    // ...
  }

  protected interface AnnotatedMethodMetadata {
    // ...
  }

  protected interface MethodMetadataBuilder {
    // ...
  }

  // 解析参数上的注解信息
  protected List requestParamMetadata(MethodMetadata data) {
    // ...
  }

  // 解析方法上的注解信息
  protected AnnotatedMethodMetadata getAnnotationMetadata(Method method) {
    // ...
  }

  // 构建 HTTP 请求模板
  protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) {
    // ...
  }
}

重要说明:AbstractContract 抽象类提供了一些公共的解析逻辑和模板构建方法,可以被子类继承和扩展。

public interface RequestInterceptor {

  // 处理请求前的预处理操作
  void apply(RequestTemplate template);
}

public interface Retryer {

  // 确定是否重试以及间隔时间
  void continueOrPropagate(RetryableException e);

  public static class Default implements Retryer {
    // ...
  }
}

public interface Target {
  // ...
  String name();
  String url();
}

public final class Request {

  // HTTP 请求方法:GET、POST、PUT、DELETE 等
  public enum HttpMethod {
    GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, TRACE;
  }

  // 构造函数,用于创建一个新的 HTTP 请求对象
  protected Request(String method, String url, Map> headers,
                    byte[] body, Charset charset) {
    // ...
  }

  // 获取 HTTP 请求方法
  public String method() {
    return method;
  }

  // 获取 HTTP 请求 URL
  public String url() {
    return url;
  }

  // 获取 HTTP 请求头部信息
  public Map> headers() {
    return Collections.unmodifiableMap(headers);
  }

  // 获取 HTTP 请求消息体字节数组
  public byte[] body() {
    return body;
  }

  // 获取 HTTP 请求消息体编码方式
  public Charset charset() {
    return charset;
  }
}

public final class Response {

  // HTTP 响应状态码:200、404、500 等
  public enum StatusType {
    INFORMATIONAL, SUCCESSFUL, REDIRECTION, CLIENT_ERROR, SERVER_ERROR, UNKNOWN;
  }

  // 构造函数,用于创建一个新的 HTTP 响应对象
  protected Response(int status, String reason, Map> headers,
                     byte[] body, Charset charset) {
    // ...
  }

  // 获取 HTTP 响应状态码
  public int status() {
    return status;
  }

  // 获取 HTTP 响应原因短语
  public String reason() {
    return reason;
  }

  // 获取 HTTP 响应头部信息
  public Map> headers() {
    return Collections.unmodifiableMap(headers);
  }

  // 获取 HTTP 响应消息体字节数组
  public byte[] body() {
    return body;
  }

  // 获取 HTTP 响应消息体编码方式
  public Charset charset() {
    return charset;
  }
}

重要说明:RequestInterceptor 接口定义了请求前的预处理操作,可以在其中添加一些公共的请求头部信息、认证信息等;Retryer 接口定义了确定是否重试以及间隔时间的逻辑,可以使用不同实现类来实现自己的重试策略;Target 接口中新增了 name()url() 方法用于获取请求目标的名称和 URL 地址信息;Request 类表示 HTTP 请求对象,包括请求方法、URL、请求头部信息、请求消息体等;Response 类表示 HTTP 响应对象,包括响应状态码、原因短语、响应头部信息、响应消息体等。

public interface ResponseMapper {

  // 将 Feign 响应转换成服务端响应
  Response map(Response response, Type type) throws IOException;

  public static class Default implements ResponseMapper {
    // ...
  }
}

重要说明:ResponseMapper 接口定义了将 Feign 客户端的响应转换成服务端响应(即将 Feign 的 Response 对象转换成服务端的原生 Response 对象)的逻辑,通常需要自定义实现类来处理自己的业务逻辑。Default 内部类是 ResponseMapper 的默认实现,它直接返回 Feign 的 Response 对象。

public interface Contract {

  List parseAndValidatateMetadata(Class targetType);

  // 构建 HTTP 请求模板
  MethodMetadata parseAndValidateMetadata(Method method);

  class Default extends AbstractContract {
    // ...
  }
}

public abstract class AbstractContract implements Contract {

  protected interface AnnotatedParamMetadata {
    // ...
  }

  protected interface AnnotatedMethodMetadata {
    // ...
  }

  protected interface MethodMetadataBuilder {
    // ...
  }

  // 解析参数上的注解信息
  protected List requestParamMetadata(MethodMetadata data) {
    // ...
  }

  // 解析方法上的注解信息
  protected AnnotatedMethodMetadata getAnnotationMetadata(Method method) {
    // ...
  }

  // 构建 HTTP 请求模板
  protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) {
    // ...
  }
}

重要说明:Contract 接口定义了 Feign 客户端接口和 HTTP 请求之间的映射规则,包括 URL、HTTP 方法、请求头、请求体等。默认情况下,OpenFeign 使用的是 Spring Cloud 中的 SpringMvcContract 实现类。AbstractContract 类提供了一些公共的解析逻辑和模板构建方法,而 Default 内部类则是 AbstractContract 的默认实现。

public interface Decoder {

  // 将响应字节数组解码成指定类型的对象
  Object decode(Response response, Type type) throws IOException, DecodeException;
}

public interface Encoder {

  // 将请求对象编码成指定格式的字节数组
  void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}

public interface Logger {

  // 日志级别:NONE、BASIC、HEADERS 和 FULL 等级别
  enum Level {
    NONE, // 不输出日志
    BASIC, // 只输出请求方法和 URL
    HEADERS, // 输出请求和响应的头部信息
    FULL // 输出完整的请求和响应信息
  }

  // 输出日志信息
  void log(String configKey, String format, Object... args);
}

重要说明:DecoderEncoder 接口分别定义了响应消息体和请求消息体的解码和编码逻辑,可以使用不同实现类来支持不同的编解码格式,比如 JSON、XML、Protobuf 等。Logger 接口定义了 Feign 客户端的日志输出格式和级别,可以使用不同实现类来支持不同的日志框架。

public interface Target {

  // 获取请求目标类型
  Class type();

  // 获取请求目标名称
  String name();

  // 获取请求目标 URL
  String url();
}

public final class HardCodedTarget implements Target {

  private final Class type;
  private final String name;

  public HardCodedTarget(Class type, String name) {
    this.type = checkNotNull(type, "type");
    this.name = checkNotNull(name, "name");
  }

  @Override
  public Class type() {
    return type;
  }

  @Override
  public String name() {
    return name;
  }

  @Override
  public String url() {
    throw new UnsupportedOperationException();
  }
}

重要说明:Target 接口表示请求目标信息,比如服务名、IP 地址、端口号等;而 HardCodedTarget 类则是对 Target 接口的默认实现。



扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1168.html

标签: feign 使用
分享给朋友:

“Open Feign 使用、原理、源码(openfeign feign)” 的相关文章

Vue3 如何实现父子组件传值?

在Vue 3中,要实现父子组件传值效果主要通过props和emit两种机制来实现,下面我们就来详细介绍一下这两种机制。父组件向子组件传值propsprops是Vue组件的一种机制,主要的作用就是实现从父组件向子组件传递数据值,在父组件上通过在子组件标签上定义属性来实现数据属性值的传递,在子组件中通过...

【Vue3 基础】05.组件化

这是 Vue3 + Vite + Pinia +TS + Element-Plus 实战系列文档。最近比较忙没什么时间写文章,争取早日把这个系列完结吧~生命周期和模板引用在本章之前,我们通过响应式 api 和声明式渲染,处理了 DOM 的更新,但光是这些,对于一些复杂的需要手动操作 DOM 的情况,...

Gitlab+Jenkins通过钩子实现自动部署web项目,图文详细教程

扩展参考:Jenkins+Gitlab通过脚本自动部署回滚web项目至集群 一:基础环境介绍及准备1):Gitlab服务器:ubuntu 192.168.152.131 ---参考搭建:Linux安装gitlab,docker安装gitlab教程2):Jenkins服务器:ubunu 192.168...

Python 幕后:Python导入import的工作原理

更多互联网精彩资讯、工作效率提升关注【飞鱼在浪屿】(日更新)Python 最容易被误解的方面其中之一是import。Python 导入系统不仅看起来很复杂。因此,即使文档非常好,它也不能让您全面了解正在发生的事情。唯一方法是研究 Python 执行 import 语句时幕后发生的事情。注意:在这篇文...

身体越柔软越好?刻苦拉伸可能反而不健康 | 果断练

坐下伸直膝盖,双手用力向前伸,再用力……比昨天前进了一厘米,又进步了! 这么努力地拉伸,每个人都有自己的目标,也许是身体健康、线条柔美、放松肌肉、体测满分,也可能为了随时劈个叉,享受一片惊呼。 不过,身体柔软,可以享受到灵活的福利,也可能付出不稳定的代价,并不是越刻苦拉伸越好。太硬或者太软,都不安全...

el-table内容\n换行解决办法

问题请求到的数据带有换行符 '\n'但页面展示时不换行statusRemark: "\"1、按期完成计划且准确率100%,得100分;\n2、各项目每延误1天,扣1分;每失误1次或者员工投诉1次,扣3分,失误层面达到公司级影响较大的,该项绩效分数为0\"\n&...