简单谈谈Feign

这篇具有很好参考价值的文章主要介绍了简单谈谈Feign。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简单谈谈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

简单谈谈Feign

原理简述

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去进行更高性能、高可用的处理远程调用文章来源地址https://www.toymoban.com/news/detail-484860.html

到了这里,关于简单谈谈Feign的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [toolschain] 怎么运用git 嵌套git 管理(子文件夹中也有个git) 并且如何简单设置使用repo的笔记 本文是求助GPT的记录 实践有用

    Q:一个文件夹a,a中的文件被a中的git 1管理,同时与a平级有一个git2,怎么让git 也能管理到git1 的内容 A:如果你想让一个 Git 仓库(git2)也能管理另一个 Git 仓库(git1)的内容,你可以使用 Git 的子模块(submodule)功能。子模块允许一个 Git 仓库包含另一个 Git 仓库,使得你可

    2024年02月04日
    浏览(65)
  • “队列” 无罪,只是太美(Java篇)

    本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️ 鱼式疯言 :❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的 大白话 , 小编会尽可能的在每个概念后插入 鱼式疯言 ,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个

    2024年04月11日
    浏览(36)
  • 工具 | Cursor:一个不只是写代码的工具

    本文首发微信公众号: 全副武装的大师兄 (一个分享前沿技术,生活感受的公众号,关注我,率先了解好玩的工具) 最新版本v0.1.12已经需要收费,伙伴们可以选择不用升级,另外,大家如果没有0.1.11的安装包,可以找我。 [写在前面的话] 朋友们,现在基于GPT3.5, GPT4的产品

    2024年02月01日
    浏览(45)
  • 图解渠道网关:不只是对接渠道的接口(一)

    这是《百图解码支付系统设计与实现》专栏系列文章中的第(20)篇。 点击上方关注,深入了解支付系统的方方面面。 主要讲清楚什么是渠道,有哪些类型的渠道,什么是渠道网关,渠道网关在支付系统中定位、核心功能、常见渠道类型、渠道网关的产品架构、系统架构等。

    2024年01月17日
    浏览(39)
  • Google用AI替代广告销售工作只是开始……

    关注卢松松,会经常给你分享一些我的经验和观点。 前几天Google不是裁员3万人吗,其中有一个信息值得关注:就是Google的广告部门的部分员工,也被裁员了。 当然这不新鲜的,主要原因是Google的广告业务正在转向AI驱动了,AI是裁员广告部门的最重要原因。 比如Google Ads产品

    2024年01月20日
    浏览(45)
  • 你的数字藏品可能真的只是一张图片

    国外 NFT 市场的火爆也同样引燃了国内的市场,像腾讯、阿里等诸多大厂纷纷入局,同时,大量中小企业也在这些头部企业的带领下聚集而来。出于政策风险隐患的防范要求,国内的区块链并不是国外的公链,而是由一个或多个机构独立部署的联盟链,同时也将 NFT 改名为

    2024年01月21日
    浏览(42)
  • AutoGPT目前只是成功学大师GPT版

    最近很多人在交流对于AutoGPT的震惊和激动。AutoGPT是一个开源的应用程序,展示了GPT-4语言模型的能力。这个程序由GPT-4驱动,自主地开发和管理业务,以增加净值。它是GPT-4完全自主运行的第一个例子,突破了人工智能的可能性。虽然AutoGPT的创新令人兴奋,但它也暴露出横行的

    2023年04月14日
    浏览(35)
  • 粘包/拆包问题一直都存在,只是到TCP就拆不动了。

    OSI open-system-Interconnection TCP/IP 5层协议栈 应用层和操作系统的边界是 系统调用 ,对应到网络编程是socket api TCP/UDP 概况 TCP粘包问题 TCP/IP报头深思 定义了网络框架,以层为单位实现协议,同时控制权逐层传递。 OSI实际并没有落地,TCP/IP 5层协议栈是目前主流的落地实现 。 TC

    2024年02月03日
    浏览(74)
  • 游戏不再只是娱乐,更成为了一种学习和成长的途径

    随着科技的飞速发展和游戏设计的创新,当下的游戏行业正经历着前所未有的繁荣时代。各种各样的游戏类型在不断涌现,为玩家们带来了丰富多彩的娱乐体验。这些火热的游戏类型不仅改变了我们的娱乐方式,还在无形中影响了我们的生活。 多元游戏类型,满足多样需求

    2024年02月12日
    浏览(80)
  • 黑盒测试可不只是点点点,也需要会用各种测试工具~

    对于不了解软件测试或者刚进行不久的人们来说,黑盒测试就是点点点,没有技术含量,但是我要说的,错!黑盒测试也是一项极具技术含量的工作! 首先说一下黑盒测试,从权威书籍《软件测试》的定义来说,黑盒测试就是把程序看作是一个不透明的黑盒子,对程序进行数

    2024年04月27日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包