简单谈谈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
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
的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);
}
@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
类去处理响应结果并返回最终结果- 其中就调用了
ResponseInterceptor
与feign
自带的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容器- 如果是
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
相关的远程调用执行流程,在运行机制以及调用性能上,满足不了生产环境的要求文章来源:https://www.toymoban.com/news/detail-484860.html
- 没有远程调用过程中的熔断监测和恢复机制;
- 也没有用到高性能的
HTTP
连接池技术
所以应该使用Hystrix
相关的HystrixInvocationHandler
去进行更高性能、高可用的处理远程调用文章来源地址https://www.toymoban.com/news/detail-484860.html
到了这里,关于简单谈谈Feign的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!