简单谈谈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
类去处理响应结果并返回最终结果 - 其中就调用了ResponseInterceptor
与feign
自带的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容器 - 如果是hystrix
或sentinel
,则构造其对应的处理器实例- 构建时,维护了一个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
去进行更高性能、高可用的处理远程调用
版权归原作者 Jay_Chou345 所有, 如有侵权,请联系我们删除。