简单谈谈Feign
文章目录
- 简单谈谈Feign
- 前言
- Feign属于RPC嘛?
- 原理简单图解
- 原理简述
- Feign.Build
- 动态代理工厂InvocationHandlerFactory
- 动态代理类FeignInvocationHandler
- 方法处理器MethodHandler
- 总结
本文只是简单粗略的分析一下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
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 {@Overridepublic 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
的map,
而MethodHandler
是InvocationHandlerFactory
的一个接口,里面包含了一个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);}@Overridepublic 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
类去处理响应结果并返回最终结果- 其中就调用了
ResponseInterceptor
与feign
自带的Decoder
去将响应进行解码操作
- 其中就调用了
final class SynchronousMethodHandler implements MethodHandler {// 执行Handler 的处理@Overridepublic Object invoke(Object[] argv) throws Throwable {// 构建请求模板RequestTemplateRequestTemplate 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容器- 如果是
hystrix
或sentinel
,则构造其对应的处理器实例 - 构建时,维护了一个
Map<Method, MethodHandler> dispatch
- 如果是
- 装配代理实例:为
- 调用阶段
- 依赖注入
feign
接口 - InvokeHandler.invoke:调用相关方法时,实际上是通过
FeignInvocationHandler
处理器实例执行invoke
方法- MethodHandler.invoke:根据方法名称从
dispatch
中拿到MethodHandler
的实现子类执行其invoke
方法(例如SynchronousMethodHandler
) - 构建请求模板
RequestTemplate
与请求参数options
- 拦截器构建请求,例如构建请求头相关参数
- 返回构建好的请求
- feign.Client完成远程 URL 请求执行和获取远程结果:
Client
接口的实现类发送请求并获取结果响应Client
实现类将获取的响应结果解码并返回
- MethodHandler.invoke:根据方法名称从
- 依赖注入
最后,默认的与 FeignInvocationHandler
相关的远程调用执行流程,在运行机制以及调用性能上,满足不了生产环境的要求
- 没有远程调用过程中的熔断监测和恢复机制;
- 也没有用到高性能的
HTTP
连接池技术
所以应该使用Hystrix
相关的HystrixInvocationHandler
去进行更高性能、高可用的处理远程调用