0


简单谈谈Feign

简单谈谈Feign

文章目录

本文只是简单粗略的分析一下

feign

的源码与过程原理

前言

Feign

Netflix

开发的声明式、模板化的

HTTP

客户端, Feign

可以

帮助我们更快捷、优雅地调用

HTTP API

Spring Cloud

Feign

进行了增强,整合了

Spring Cloud Ribbon

Spring 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
publicabstractclassBaseBuilder<BextendsBaseBuilder<B>>{// 请求拦截器protectedfinalList<RequestInterceptor> requestInterceptors =newArrayList<>();// 响应拦截器protectedResponseInterceptor responseInterceptor =ResponseInterceptor.DEFAULT;// 日志记录级别protectedLogger.Level logLevel =Logger.Level.NONE;// 规则解析器protectedContract contract =newContract.Default();// 重试机制器protectedRetryer retryer =newRetryer.Default();// 日志protectedLogger logger =newNoOpLogger();// 编码器protectedEncoder encoder =newEncoder.Default();// 解码器protectedDecoder decoder =newDecoder.Default();protectedboolean closeAfterDecode =true;protectedQueryMapEncoder queryMapEncoder =newFieldQueryMapEncoder();protectedErrorDecoder errorDecoder =newErrorDecoder.Default();protectedOptions options =newOptions();// 动态代理工厂类protectedInvocationHandlerFactory invocationHandlerFactory =newInvocationHandlerFactory.Default();protectedboolean dismiss404;protectedExceptionPropagationPolicy propagationPolicy = NONE;protectedList<Capability> capabilities =newArrayList<>();}

动态代理工厂InvocationHandlerFactory

Feign

的组件中,所有的动态代理类对象都是通过

InvocationHandlerFactory

工厂完成的

InvocationHandlerFactory

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

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

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

在微服务启动时,

Feign

会进行包扫描,对加

@FeignClient

注解的接口,按照注解的规则,创建远程接口的本地

JDK Proxy

代理实例。

然后,将这些本地

Proxy

代理实例,注入到

Spring IOC

容器中。当远程接口的方法被调用,由

Proxy

代理实例去完成真正的远程访问,并且返回结果

publicinterfaceInvocationHandlerFactory{InvocationHandlercreate(Target target,Map<Method,MethodHandler> dispatch);interfaceMethodHandler{Objectinvoke(Object[] argv)throwsThrowable;}staticfinalclassDefaultimplementsInvocationHandlerFactory{@OverridepublicInvocationHandlercreate(Target target,Map<Method,MethodHandler> dispatch){returnnewReflectiveFeign.FeignInvocationHandler(target, dispatch);}}}

动态代理类FeignInvocationHandler

我们平时在使用

JDK

的动态代理时都是

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

也不例外,它有一个

ReflectiveFeign

类,其中有着一个实现了InvocationHandler接口并实现了invoke方法的静态类

FeignInvocationHandler

而这个

FeignInvocationHandler

就是被动态代理工厂类

InvocationHandlerFactory

去创建的(见上文代码)

我们应该也注意到了这个成员变量

Map<Method, MethodHandler> dispatch

它维护了一个

method

对应

MethodHandler

的map

MethodHandler

InvocationHandlerFactory

的一个接口,里面包含了一个

invoke

方法,那么其作用也很明显了

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

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

publicclassReflectiveFeignextendsFeign{staticclassFeignInvocationHandlerimplementsInvocationHandler{privatefinalTarget target;privatefinalMap<Method,MethodHandler> dispatch;FeignInvocationHandler(Target target,Map<Method,MethodHandler> dispatch){this.target =checkNotNull(target,"target");this.dispatch =checkNotNull(dispatch,"dispatch for %s", target);}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{if("equals".equals(method.getName())){try{Object otherHandler =
                  args.length >0&& args[0]!=null?Proxy.getInvocationHandler(args[0]):null;returnequals(otherHandler);}catch(IllegalArgumentException e){returnfalse;}}elseif("hashCode".equals(method.getName())){returnhashCode();}elseif("toString".equals(method.getName())){returntoString();}// 重点在这return dispatch.get(method).invoke(args);}}}
方法处理器MethodHandler

InvocationHandlerFactory

接口中的一个很简单的接口,只有一个

invoke

方法

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

URL

的响应结果

interfaceMethodHandler{Objectinvoke(Object[] argv)throwsThrowable;}

其有两个实现类

DefaultMethodHandler

(这个暂时忽略)与

SynchronousMethodHandler

SynchronousMethodHandler

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

我们这里来说一下

SynchronousMethodHandler

类是如何去处理远程请求与结果响应的

  • 构建请求模板RequestTemplate与请求参数
  • 通过请求模板构建请求 - 拦截器构建请求,例如构建请求头相关参数
  • 通过Client接口的实现类发送请求并获取结果响应 - 值得一提的是,Client接口有很多实现类,例如其本身default类、LoadBalancerFeignClient
  • AsyncResponseHandler类去处理响应结果并返回最终结果 - 其中就调用了ResponseInterceptorfeign自带的Decoder去将响应进行解码操作
finalclassSynchronousMethodHandlerimplementsMethodHandler{// 执行Handler 的处理@OverridepublicObjectinvoke(Object[] argv)throwsThrowable{// 构建请求模板RequestTemplateRequestTemplate template = buildTemplateFromArgs.create(argv);// 构建请求参数Options options =findOptions(argv);// 获取重试机制器Retryer retryer =this.retryer.clone();while(true){try{// 执行请求并解码returnexecuteAndDecode(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;}}}// 执行请求,然后解码结果ObjectexecuteAndDecode(RequestTemplate template,Options options)throwsThrowable{// 构建请求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));}throwerrorExecuting(request, e);}long elapsedTime =TimeUnit.NANOSECONDS.toMillis(System.nanoTime()- start);if(decoder !=null){return responseInterceptor
              .aroundDecode(newInvocationContext(decoder, metadata.returnType(), response));}CompletableFuture<Object> resultFuture =newCompletableFuture<>();
        asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
            metadata.returnType(), elapsedTime);try{if(!resultFuture.isDone())thrownewIllegalStateException("Response handling not done");return resultFuture.join();}catch(CompletionException e){Throwable cause = e.getCause();if(cause !=null)throw cause;throw e;}}// RequesttargetRequest(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://blog.csdn.net/Jay_Chou345/article/details/125951017
版权归原作者 Jay_Chou345 所有, 如有侵权,请联系我们删除。

“简单谈谈Feign”的评论:

还没有评论