SpringCloud-技术专区-从源码层面让你认识Feign工作流程和运作机制

时间:2021-08-19 08:35:24   收藏:0   阅读:17

Feign工作流程源码解析

什么是feign:一款基于注解和动态代理的声明式restful http客户端。

原理

Feign发送请求实现原理

解析@FeignClient注解,生成MethodHandler

具体的解析类是ParseHandlerByName。这个类是ReflectiveFeign的内部类。

// 解析注解元数据,使用Contract解析
List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(key.type());

拿到注解元数据以后,循环处理注解元数据,创建每个方法对应的MethodHandler,这个MethodHandler最终会被代理对象调用。最终MethodHandler都会保存到下面这个集合中,然后返回。

Map<String, MethodHandler> result = new LinkedHashMap();
解析完成以后,调用ReflectiveFeign.newInstance()生成代理类。

MethodHandler是feign的一个接口,这个接口的invoke方法,是动态代理调用者InvocationHandler的invoke()方法最终调用的方法。

重新表述一遍:InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求。这就是Feign动态代理的具体实现。

ReflectiveFeign类的newInstance()方法的第57行:
// 创建动态代理调用者
InvocationHandler handler = this.factory.create(target, methodToHandler);
// 反射生成feign接口代理
T proxy = Proxy.newProxyInstance(加载器, 接口数组, handler);

InvocationHandler.invoke()的具体实现在FeignInvocationHandler.invoke(),FeignInvocationHandler也是ReflectiveFeign的一个内部类。里面有很多细节处理这里不再赘述,我们直接进入核心那一行代码,以免影响思路,我们是理Feign的实现原理的!不要在意这些细节!

// InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求

ReflectiveFeign类的invoke()方法,第323行,代码的后半段,如下:
(MethodHandler)this.dispatch.get(method). invoke(args);
MethodHandler.invoke()的具体实现:SynchronousMethodHandler.invoke()

到了这里,就是发送请求的逻辑了。发送请求前,首先要创建请求模板,然后调用请求拦截器RequestInterceptor进行请求处理。

// 创建RequestTemplate
RequestTemplate template = this.buildTemlpateFromArgs.create(argv);
// 创建feign重试器,进行失败重试
Retryer retryer = this.retryer.clone();
while(true){
    try{
        // 发送请求
        return this.executeAndDecode(template);
    } catch(RetryableException var5) {
        // 失败重试,最多重试5次
        retryer.continueOrPropagate();
    }
}
RequestTemplate处理

RequestTemplate模板需要经过一系列拦截器的处理,主要有以下拦截器:

feign.compression.request.enabled=true
feign.compression.request.min-request-size=2048

min-request-size是通过FeignClientEncodingProperties来解析的,默认值是2048。

我们还可以自定义请求拦截器,我们自定义的拦截器,也会在此时进行调用,所有实现了RequestTemplate接口的类,都会在这里被调用。比如我们可以自定义拦截器把全局事务id放在请求头里。

使用feign.Request把RequestTemplate包装成feign.Request

feign.Request由5部分组成:

http请求客户端

Feign发送http请求支持下面几种http客户端:

// 具体实现有2个类Client.Default 和LoadBalancerFeignClient

response = this.client.execute(request, this.options);

Client接口定义了execute()的接口,并且通过接口内部类实现了Client.execute()。

HttpURLConnection connection = this.convertAndSend(request, options);

return this.convertResponse(connection).toBuilder(). request(request).build();

这种方式是最简单的实现,但是不支持负载均衡,Spring Cloud整合了Feign和Ribbon,所以自然会把Feign和Ribbon结合起来使用。也就是说,Feign发送请求前,会先把请求再经过一层包装,包装成RibbonRequest。

也就是发送请求的另一种实现LoadBalancerFeignClient。

// 把Request包装成RibbonRequest
RibbonRequest ribbonRequest = new   (this.delegate, request, uriWithoutHost);
// 配置超时时间
IClientConfig requestConfig = this.getClientConfig(options, clientName);
// 以负载均衡的方式发送请求
return ((RibbonResponse)this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
以负载均衡的方式发送请求

AbstractLoadBalancerAwareClient类的executeWithLoadBalancer()方法的第54行:

Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));

AbstractLoadBalancerAwareClient实现了IClient接口,该接口定义了execute()方法,

我们以RibbonLoadBalancingHttpClient为例来说明,RibbonLoadBalancingHttpClient.execute()

第62行代码:

// 组装HttpUriRequest

HttpUriRequest httpUriRequest = request.toRequest(requestConfig);

// 发送http请求

HttpResponse httpResponse = ((HttpClient)this.delegate).execute(httpUriRequest);

// 使用RibbonApacheHttpResponse包装http响应信息

return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());

RibbonApacheHttpResponse由2部分组成:

httpResponse

uri

处理http相应

http请求经过上面一系列的转发以后,最终还会回到SynchronousMethodHandler,然后SynchronousMethodHandler会进行一系列的处理,然后响应到浏览器。

Feign架构图

技术分享图片

原文:https://www.cnblogs.com/liboware/p/15158925.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!