简单谈谈Feign

22 篇文章 1 订阅
4 篇文章 0 订阅

简单谈谈Feign

本文只是简单粗略的分析一下feign的源码与过程原理

前言

FeignNetflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API

Spring CloudFeign进行了增强,整合了Spring Cloud RibbonSpring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。

Feign属于RPC嘛?

市面上的基于RPC的开源框架更多的是指基于TCP(或其他协议)、自带服务治理等等等等

由此可见,feign也许并不完全属于RPC的概念,可是今天看到一个博主写的一句话我觉得说的很对

其实判断RPC尽可以不那么复杂,你像调本地接口一样调用远程接口的方式,那它就是RPC

RPC的初衷就是让开发者在实现对其他服务的远程调用时,可以尽可能地减少对于网络通信等层面的关心,而专注于业务上的实现,所以其实不管它是基于HTTP还是基于TCP还是基于啥啥啥的,既然它为我们简化了使用,那我就认为它是一款合格的“RPC框架”

原理简单图解

原理简述

Feign.Build

public abstract class Feign类下有一个继承了BaseaBuilder类的类public static class Builder extends BaseBuilder<Builder>

在老版本的feign中好像是没有这个BaseBuilder的,而是将一些成员变量直接放在Builder

在新版Feign中才将这些成员变量放到了BaseBuilder中,然后用Builder去继承BaseBuilder

public abstract class BaseBuilder<B extends BaseBuilder<B>> {
    // 请求拦截器
    protected final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
    // 响应拦截器
    protected ResponseInterceptor responseInterceptor = ResponseInterceptor.DEFAULT;
    // 日志记录级别
    protected Logger.Level logLevel = Logger.Level.NONE;
    // 规则解析器
    protected Contract contract = new Contract.Default();
    // 重试机制器
    protected Retryer retryer = new Retryer.Default();
    // 日志
    protected Logger logger = new NoOpLogger();
    // 编码器
    protected Encoder encoder = new Encoder.Default();
    // 解码器
    protected Decoder decoder = new Decoder.Default();

    protected boolean closeAfterDecode = true;

    protected QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();

    protected ErrorDecoder errorDecoder = new ErrorDecoder.Default();

    protected Options options = new Options();

    // 动态代理工厂类
    protected InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();

    protected boolean dismiss404;

    protected ExceptionPropagationPolicy propagationPolicy = NONE;

    protected List<Capability> capabilities = new ArrayList<>();
}

动态代理工厂InvocationHandlerFactory

Feign的组件中,所有的动态代理类对象都是通过InvocationHandlerFactory工厂完成的

InvocationHandlerFactory类中有一个实现了InvocationHandlerFactory接口并重写了create方法的实现类,用于创建动态代理处理器对象

这里不仅仅只有一个用于构建FeignInvocationHandler代理的默认类

也可以由HystrixFeign、SentinelFeign去实现这个create方法

在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。

然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}
动态代理类FeignInvocationHandler

我们平时在使用JDK的动态代理时都是

  • 新建一个处理类并实现InvocationHandler接口
  • 实现invoke方法,作为被代理接口的方法代理实现

feign也不例外,它有一个ReflectiveFeign类,其中有着一个实现了InvocationHandler接口并实现了invoke方法的静态类FeignInvocationHandler

而这个FeignInvocationHandler就是被动态代理工厂类InvocationHandlerFactory去创建的(见上文代码)

我们应该也注意到了这个成员变量Map<Method, MethodHandler> dispatch

它维护了一个method对应MethodHandler的mapMethodHandlerInvocationHandlerFactory的一个接口,里面包含了一个invoke方法,那么其作用也很明显了

在处理远程方法调用的时候,执行FeignInvocationHandler的invoke方法

然后通过Java反射的方法作为key,在dispatch 映射对象中,找到对应的MethodHandler 方法处理器,然后交给MethodHandler的invoke方法来完成实际的HTTP请求和结果的处理

public class ReflectiveFeign extends Feign {  
    static class FeignInvocationHandler implements InvocationHandler {
        private final Target target;
        private final Map<Method, MethodHandler> dispatch;
        
        FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
          this.target = checkNotNull(target, "target");
          this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          if ("equals".equals(method.getName())) {
            try {
              Object otherHandler =
                  args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
              return equals(otherHandler);
            } catch (IllegalArgumentException e) {
              return false;
            }
          } else if ("hashCode".equals(method.getName())) {
            return hashCode();
          } else if ("toString".equals(method.getName())) {
            return toString();
          }
          // 重点在这
          return dispatch.get(method).invoke(args);
        }
      }
}
方法处理器MethodHandler

InvocationHandlerFactory接口中的一个很简单的接口,只有一个invoke方法

主要职责是完成实际远程URL请求,然后返回解码后的远程URL的响应结果

  interface MethodHandler {
      Object invoke(Object[] argv) throws Throwable;
  }

其有两个实现类DefaultMethodHandler(这个暂时忽略)与SynchronousMethodHandler

SynchronousMethodHandler实现类提供了基本的远程URL的同步请求处理

我们这里来说一下SynchronousMethodHandler类是如何去处理远程请求与结果响应的

  • 构建请求模板RequestTemplate与请求参数
  • 通过请求模板构建请求
    • 拦截器构建请求,例如构建请求头相关参数
  • 通过Client接口的实现类发送请求并获取结果响应
    • 值得一提的是,Client接口有很多实现类,例如其本身default类、LoadBalancerFeignClient
  • AsyncResponseHandler类去处理响应结果并返回最终结果
    • 其中就调用了ResponseInterceptorfeign自带的Decoder去将响应进行解码操作
final class SynchronousMethodHandler implements MethodHandler {
  	// 执行Handler 的处理
	@Override
  	public Object invoke(Object[] argv) throws Throwable {
        // 构建请求模板RequestTemplate
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        // 构建请求参数
        Options options = findOptions(argv);
        // 获取重试机制器
        Retryer retryer = this.retryer.clone();
        while (true) {
          try {
            // 执行请求并解码
            return executeAndDecode(template, options);
          } catch (RetryableException e) {
            try {
              retryer.continueOrPropagate(e);
            } catch (RetryableException th) {
              Throwable cause = th.getCause();
              if (propagationPolicy == UNWRAP && cause != null) {
                throw cause;
              } else {
                throw th;
              }
            }
            if (logLevel != Logger.Level.NONE) {
              logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
          }
        }
  	}

  	// 执行请求,然后解码结果
	Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        // 构建请求
        Request request = targetRequest(template);
        
        Response response;
        long start = System.nanoTime();
        try {
          // 执行请求,并获取结果
          response = client.execute(request, options);
          response = response.toBuilder()
              .request(request)
              .requestTemplate(template)
              .build();
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
          }
          throw errorExecuting(request, e);
        }
        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

        if (decoder != null) {
          return responseInterceptor
              .aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));
        }

        CompletableFuture<Object> resultFuture = new CompletableFuture<>();
        asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
            metadata.returnType(), elapsedTime);

        try {
          if (!resultFuture.isDone())
            throw new IllegalStateException("Response handling not done");
          return resultFuture.join();
        } catch (CompletionException e) {
          Throwable cause = e.getCause();
          if (cause != null)
            throw cause;
          throw e;
        }
    }
    // 
    Request targetRequest(RequestTemplate template) {
        // 拦截器构建请求,例如构建请求头相关参数
        for (RequestInterceptor interceptor : requestInterceptors) {
          interceptor.apply(template);
        }
        // 返回构建好的请求
        return target.apply(template);
  	}
}

总结

具体的代码就不贴了,源码里都有,最后再简单总结一下Feign的调用流程

  • 初始化阶段
    • 装配代理实例:为@FeignClient注解的所有远程接口通过InvocationHandlerFactory构建FeignInvocationHandler的处理器实例到IOC容器
      • 如果是hystrixsentinel,则构造其对应的处理器实例
      • 构建时,维护了一个Map<Method, MethodHandler> dispatch
  • 调用阶段
    • 依赖注入feign接口
    • InvokeHandler.invoke:调用相关方法时,实际上是通过FeignInvocationHandler处理器实例执行invoke方法
      • MethodHandler.invoke:根据方法名称从dispatch中拿到MethodHandler的实现子类执行其invoke方法(例如SynchronousMethodHandler
      • 构建请求模板RequestTemplate与请求参数options
        • 拦截器构建请求,例如构建请求头相关参数
        • 返回构建好的请求
      • feign.Client完成远程 URL 请求执行和获取远程结果
        • Client接口的实现类发送请求并获取结果响应
        • Client实现类将获取的响应结果解码并返回

最后,默认的与 FeignInvocationHandler 相关的远程调用执行流程,在运行机制以及调用性能上,满足不了生产环境的要求

  • 没有远程调用过程中的熔断监测和恢复机制;
  • 也没有用到高性能的HTTP连接池技术

所以应该使用Hystrix相关的HystrixInvocationHandler去进行更高性能、高可用的处理远程调用

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Feign是一个声明性的Web服务客户端,它可以让我们以类似Controller调用Service的方式来调用微服务。通过Feign,我们可以更简单、便捷地调用HTTP API,而不用自己拼接URL和参数。 下面是一个使用Feign进行服务调用的简单实例: 首先,我们需要创建一个消费者客户端的Maven项目。然后,在项目的pom.xml文件中添加Feign的依赖。 接着,我们需要创建一个接口,使用@FeignClient注解来声明该接口是一个Feign客户端,并指定要调用的服务名称。 在接口中,我们可以定义对应服务的各个接口方法,并使用@RequestMapping注解来指定具体的请求路径和参数。 最后,我们可以在其他的组件中,通过注入该Feign客户端接口来使用Feign进行服务调用。 这样,我们就可以通过Feign简单地实现服务之间的调用了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringCloud Feign 服务调用的实现](https://download.csdn.net/download/weixin_38622611/12752078)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [springcloud通过Feign做服务调用实例](https://blog.csdn.net/haokelaicds/article/details/125197476)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值