Feign的工作原理

文章目录

  • Feign的简单介绍
  • Feign的工作原理
    • 1.创建远程接口的本地代理实例
    • 2.封装Request对象并进行编码
    • 3.feign.Client发送请求并对获取结果进行解码
  • 总结

Feign的简单介绍

Feign组件主要用于微服务项目中,用来简化服务之间的远程调用,相信大家对他的使用方法已经相当熟悉了。那么Feign到底是如何实现服务的远程调用的呢?又是如何发送请求的呢?发送的请求又是什么样子的呢?本文主要就针对这三个对Feign的原理做一个简单的分析。

Feign的工作原理
上面这张图是我按照自己的理解简单的画了一下Feign的主要工作流程,下面将结合Feign底层的源码对工作原理进行详细的分析。

Feign的工作原理

根据上图我将流程分为主要的三个步骤:通过动态代理在本地实例化远程接口->封装Request对象并进行编码->发送请求并对获取结果进行解码。我写了一个简单Feign调用的Demo,代码如下,以此作为背景再结合底层源码来分析。

@FeignClient("product")
public interface ProductClient {/*** 根据商品id获取商品信息* @param id    商品id* @return  商品信息*/@GetMapping("/product/getProductInfo/{id}")Result<ProductInfoDTO> getProductInfo(@PathVariable("id") Integer id);
}

1.创建远程接口的本地代理实例

要使用Feign就必须在启动类上加上注解@EnableFeignCleints,这个注解就相当于Feign组件的一个入口,当使用@EnableFeignCleints后,程序启动后,会进行包扫描,扫描所有被@FeignCleint注解修饰的接口,通过JDK底层的动态代理来为远程接口创建代理实例,并且注册到IOC容器中。我们先来看看@EnableFeignCleints这个注解。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {//一系列属性......
}

@EnableFeignCleints注解引入了FeignClientsRegistrar类,这个类会在启动时调用registerBeanDefinitions()方法,而这个方法里面只有两行调用其他方法的代码,分别是用来加载Feign的各项配置的方法registerDefaultConfiguration(metadata, registry)和注册实例化对象的方法registerFeignClients(metadata, registry)。我们重点来看一下registerFeignClients()方法,方法的主要的逻辑就是先扫描基础包路径然后,然后找出被@ FeignClient注解修饰的接口,放入Set中,接着遍历集合为每个接口逐一创建实例化对象,主要代码如下:

//获取扫描的包路径
Set<String> basePackages;
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
......
//遍历包路径搜索目标接口
for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());registerClientConfiguration(registry, name,attributes.get("configuration"));//为接口创建代理对象的方法registerFeignClient(registry, annotationMetadata, attributes);}
}

可以看到创建对象是交给registerFeignClient()方法来处理的,该方法会在创建对象前进行一系列的准备工作,比如IOC中对象对应的key值统一使用@FeignClient中的value值+”FeignClient”,本例中对应为productFeignClient等。而对于创建对象的所调用的方法一层层深入进去会发现,最终实际创建对象的核心代码是feign.ReflectiveFeign类中的newInstance()方法,其代码如下:

List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
InvocationHandler handler = factory.create(target, methodToHandler);T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;

看到这段代码相信大家都很熟悉了吧,这不正是JDK底层典型的动态代理嘛,可见Feign也是基于代理模式来创建接口的实例化对象的。而代理工具类InvocationHandler,Feign也默认提供了两种实现,稍后会提到。

2.封装Request对象并进行编码

当调用ProductClient接口中的getProductInfo()方法时,底层通过JDK动态代理交由Feign的代理类FeignInvocationHandler进行处理,(Feign底层实现了两个调用处理器分别为FeignInvocationHandlerHystrixInvocationHandler,默认的调用处理器为FeignInvocationHandler,当和Hystrix结合使用时才会使用HystrixInvocationHandler,本篇基于默认的FeignInvocationHandler进行分析)根据方法的对象在映射集合中找到对应的MethodHandler方法处理器。

static class FeignInvocationHandler implements InvocationHandler {private final Map<Method, MethodHandler> dispatch;@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {......//找到对应的方法处理器return dispatch.get(method).invoke(args);}
}

Feign中为MethodHandler接口提供了默认的实现类SynchronousMethodHandler,其中中核心的逻辑就是根据具体的配置先创建RequestTemplate请求模板对象,然后调用Target接口中的apply(RequestTemplate input)方法根据刚刚获取到的请求模板对象创建远程服务请求的Request对象。部分核心代码如下:

final class SynchronousMethodHandler implements MethodHandler {......public Object invoke(Object[] argv) throws Throwable {//创建请求模板对象,同时使用Encode接口进行编码操作RequestTemplate template = buildTemplateFromArgs.create(argv);Retryer retryer = this.retryer.clone();//Feign默认提供的重试机制while (true) {try {//发送请求和获取响应结果的核心方法return executeAndDecode(template);} catch (RetryableException e) {retryer.continueOrPropagate(e);if (logLevel != Logger.Level.NONE) {logger.logRetry(metadata.configKey(), logLevel);}continue;}}}Object executeAndDecode(RequestTemplate template) throws Throwable {//多个方法层层调用这里简单贴出主要逻辑for (RequestInterceptor interceptor : requestInterceptors) {interceptor.apply(template);}Request request = target.apply(new RequestTemplate(template));if (logLevel != Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);}......}
}

经过这一系列的操作后,Request对象就封装好了,我们来简单看一下这个对象的具体结构:

//请求方法,本例中对应为GET
private final String method;
//要访问的接口地址,本例中对应为/product/getProductInfo/22
private final String url;
//请求头部信息,本例对应的键值对为token=xingren
private final Map<String, Collection<String>> headers;
//请求体,一般POST请求下将复杂对象放入请求体,会被转换成字节数组进行传输
private final byte[] body;
//字符集
private final Charset charset;

3.feign.Client发送请求并对获取结果进行解码

这一步骤Feign的代码量比较多,而且用了大量的try-catch和if-else,整段拿出来展示的话,可读性比较低,我在这里将源码分段进行分析。首先是调用feign.Client接口中的excute()方法执行请求并获取响应结果。对于Client接口,Feign提供了几个不同的实现:(1)默认的实现Default()类,内部使用JDK中的HttpURLConnnection完成URL请求处理,没有使用连接池,http连接的服用能力弱,性能低,一般不会采用这种方式。(2)当Feign配合Ribbon使用时提供的默认实现类LoadBalancerFeignClient,内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client客户端实现类。除此之外,我们也可以使用其他的http客户端来替代Feign底层默认的实现类。比如ApacheHttpClient,相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口。既提高了开发的效率,也方便提高代码的健壮性。使用起来也很简单,只需引入依赖,然后增加配置feign.httpclient.enabled=true即可。这一步骤,源码简化之后如下:

Response response;
try { response = client.execute(request, options);response.toBuilder().request(request).build();
} catch (IOException e) {//一系列的日志处理
}

其次是对响应对象的null值处理、响应种类的判断以及响应对象消息体的处理,最后会将body转化成字节数组:

//判断返回的响应对象的类型是否正确
if (Response.class == metadata.returnType()) {//消息体的非空判断if (response.body() == null) {return response;}if (response.body().length() == null || response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {shouldClose = false;return response;}//转换成字节数组byte[] bodyData = Util.toByteArray(response.body().asInputStream());return response.toBuilder().body(bodyData).build();
}

最后一步是针对不同的http状态码来做不同的处理,如果http状态码为成功,再判断是否有返回值,如果有返回值就对响应对象进行解码操作,无返回值直接返回null;如果是404调用解码方法,最后Decoder底层会执行emptyValueOf()方法返回空的Response对象;如果状态码为其他类型,则返回异常。

if (response.status() >= 200 && response.status() < 300) {if (void.class == metadata.returnType()) {return null;} else {return decode(response);}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {return decode(response);
} else {throw errorDecoder.decode(metadata.configKey(), response);
}

总结

本文对Feign主要的工作流程做了一些简单的分析,其实关于Feign的原理还有很多细节值得深入探究。另外关于Feign的相关配置本文都省略了,后续将陆续更新Feign使用中的坑以及Feign的各种配置。

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

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

相关文章

OpenFeign和feign使用简介

1.OpenFeign简介 Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解&#xff0c;比如&#xff1a;FeignClient注解。Feign有可插拔的注解&#xff0c;包括Feign注解和JAX-RS注解。 Feign也支持编码…

Feign详解与实例

基本介绍 Feign是一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样&#xff0c;从避免了调用目标微服务时&#xff0c;需要不断的解析/封装json 数据的繁琐。Feign集成了Ribbon。Ribboneureka是面向微服务编程&#xff0c;而Feign是面向接口编程。 Fegin是一个…

FeignClient

FeignClient的调用 注意&#xff1a; 依赖可能会出现冲突&#xff0c;请根据spring-cloud版本选择并修改 1. 引入依赖&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artif…

Feign的介绍

Feign是springcloud里面的一个功能组件&#xff0c;那么它是实现一个什么功能呢&#xff1f; 首先我们可以先从字面意思上去理解一下它&#xff0c;Feign&#xff0c;英文翻译过来就是伪装的意思&#xff0c;实际上它的功能也是和伪装相关的&#xff0c;在我们之前在客户端配置…

@FeignClient使用详解

FeignClient标签的常用属性如下&#xff1a; name&#xff1a;指定FeignClient的名称&#xff0c;如果项目使用了Ribbon&#xff0c;name属性会作为微服务的名称&#xff0c;用于服务发现url: url一般用于调试&#xff0c;可以手动指定FeignClient调用的地址decode404:当发生h…

Feign详解

一. Feign概述 Feign是Spring Cloud提供的声明式、模板化的HTTP客户端&#xff0c; 它使得调用远程服务就像调用本地服务一样简单&#xff0c;只需要创建一个接口并添加一个注解即可。 Spring Cloud集成Feign并对其进行了增强&#xff0c;使Feign支持了Spring MVC注解&#x…

Feign(简介和使用)

1. Feign介绍 通过RestTemplate调用其它服务的API时&#xff0c;所需要的参数须在请求的URL中进行拼接&#xff0c;如果参数少的话或许我们还可以忍受&#xff0c;一旦有多个参数的话&#xff0c;这时拼接请求字符串就会效率低下 Feign是一个声明式的Web Service客户端&#…

什么是Feign?

服务间调用介绍 现有的服务调用方式 利用拼接的方式。 虽然上面有的用不错就很好了 Feign解决了什么问题 Feign的调用方式 Feign体系架构解析-武装到牙齿 上一节我们了解了feign的主要功能&#xff0c;它就像一个自拍杆一样&#xff0c;方便了Eureka的远程调用。可是怎么看…

简单理解Feign的原理与使用

文章目录 SpringCloud 总架构图一、简介1.1、负载均衡的概念2.2、Feign概念 二、入门案例2.1、导入依赖2.2、Feign的客户端2.3、调用Feign2.4、开启Feign功能2.5、启动测试2.6、Feign实现原理简单分析 三、负载均衡(Ribbon)四、熔断器支持五、请求压缩和响应压缩六、配置日志级…

Feign基本使用(超详细)

目录 一、Feign概述 二、Feign入门 1.创建服务提供者(provider) 2.创建feign接口 3、创建服务消费者(consumer) 三、Feign 原理 四、Feign优化 1、开启feign日志 2、feign超时问题 3、http连接池 4、gzip压缩 前言 当我们通过RestTemplate调用其它服务的API时&#xff0c;所…

中外黑客两则

外国一则&#xff1a;丹尼斯麦卡利斯泰尔里奇。挑选他的原因&#xff1a;帅。 &#xff08;英语&#xff1a;Dennis MacAlistair Ritchie&#xff1b;1941年9月9日&#xff0d;2011年10月12日[3][4]&#xff09;&#xff0c;著名的美国计算机科学家&#xff0c;对C语言和其他编…

git使用X篇_2_Git全套教程IDEA版(git、GitHub、Gitee码云、搭建公司内部GitLab、与IDEA集成等内容)

本文是根据以下视频及网上总结进行更新后的介绍git使用的博文。包含了git、GitHub、Gitee码云、搭建公司内部GitLab、与IDEA集成等内容。 笔记来源&#xff1a;【尚硅谷】5h打通Git全套教程IDEA版&#xff08;涵盖GitHub\Gitee码云\GitLab&#xff09; 文章目录 初识 Git0、内容…

Axure原型模板、元件库、组件库

在产品设计中&#xff0c;随时会使用到相似场景&#xff0c;每次都反复绘制耗时耗力。 若使用通用模板即可快速提高设计效率。 下面给大家提供一个登录多种验证系统框架模板&#xff0c;可快速修改使用。 预览链接&#xff1a; Axure高保真原型设计、Axure元件库、产品交互…

AXURE9最全的WEB设计元件库(分享版).rplib

Axure9常用到的WEB设计元件库&#xff0c;在Axure点击号即可导入元件库使用&#xff0c;或者打开Axure R9安装目录&#xff0c;进入DefaultSettings\Libraries&#xff0c;复制资源到此目录并重启Axure软件即可看到。 文件&#xff1a;590m.com/f/25127180-481124286-469239 &…

Axure移动端通用元件库rplib格式包含安卓、苹果各种主流手机、平板线框图元件库、IOS系统图标、人物图标、导航和分页、表格元素、各种小图标、移动元件库、axure元件库、axure原型

Axure移动端通用元件库rplib格式包含安卓、苹果各种主流手机、平板线框图元件库、IOS系统图标、人物图标、导航和分页、表格元素、各种小图标等 移动端通用元件库、app通用元件库、数据展示、操作反馈、通用模板、数据录入、列表页、表单页、详情页、通用版布局、移动端手机模…

Axure导入元件库和使用

下载元件库 vant 元件库下载: Vant - Mobile UI Components built on Vue element UI 元件库下载:https://element.eleme.cn/#/zh-CN/resource 以vant示例&#xff0c;下载完成后解压 我们看到有.rp和.rplib格式的文件 rp文件可以理解为一个别人设计好的原型作品。 rplib是原…

【Axure9.0原型实战(一)】Axure9.0的元件库的使用、导入、制作、路径等操作方法与技巧(附Axure元件库大全)

Axure9.0的元件库 文章目录 Axure9.0的元件库Axure9.0的元件库大全1.windows系统加载元件库2.MAC系统加载元件库3.新加入一些元件库4.自己建立元件库 Axure9.0的元件库大全 链接&#xff1a;https://pan.baidu.com/s/18VWM9R1qgFAXXz8Xooq4-A 提取码&#xff1a;u2gx 1.windo…

Axure RP9 的元件库

Axure的元件库 1.加载元件库 可以通过导入一些现有的元件库&#xff0c;来提高绘制的效率及美观性。 &#xff08;1&#xff09;windows系统&#xff1a; 将准备好的元件库复制到Axure 根目录下-【DefaultSettings】-【Libraries】中&#xff1b;移动成功后&#xff0c;重启…

嵌入式linux音乐电子相册制作软件,ApowerShow – 超级轻便的在线照片音乐视频制作软件...

有没有什么好用的照片视频制作软件呢&#xff1f;我大概对比了一下国内的其他软件&#xff0c;发现转场效果很多&#xff0c;但是需要自己去添加。它们的界面感觉有点繁琐&#xff0c;不是那么简洁。 其实除了使用国内的照片视频制作软件&#xff0c;你还可以使用ApowerShow。它…

html立体音乐相册源码,印记工坊立体音乐相册 v 1.8 官方版|印记工坊立体音乐相册官方版|印记工坊立体音乐相册电脑版_最火软件站...

印记工坊立体音乐相册包含多种多样的模板可以供用户使用&#xff0c;制作多种样式的电子相册,是一款傻瓜式视频音乐相册制作工具&#xff0c;及时是毫无制作经验的用户也能创作出精美的相册。最火软件站提供下载。 软件特色 故事视频制作 MV展示视频相册 时间轴视频制作 时光轴…