努力尽今夕,少年犹可夸。这篇文章主要讲述SpringCloud技术专题「Feign」从源码层面让你认识Feign工作流程和运作机制相关的知识,希望能为你提供帮助。
Feign工作流程源码解析原理
Feign发送请求实现原理
- 微服务启动类上标记@EnableFeignClients注解,然后Feign接口上标记@FeignClient注解。@FeignClient注解有几个参数需要配置,这里不再赘述,都很简单。
- Feign框架会扫描注解,然后通过Feign类来处理注解,并最终生成一个Feign对象。
// 解析注解元数据,使用Contract解析
List<
MethodMetadata>
metadata = https://www.songbingjia.com/android/this.contract.parseAndValidateMetadata(key.type());
Map<
String, MethodHandler>
result = new LinkedHashMap();
解析完成以后,调用ReflectiveFeign.newInstance()生成代理类。重新表述一遍:InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求。这就是Feign动态代理的具体实现。
ReflectiveFeign类的newInstance()方法的第57行:
// 创建动态代理调用者
InvocationHandler handler = this.factory.create(target, methodToHandler);
// 反射生成feign接口代理
T proxy = Proxy.newProxyInstance(加载器, 接口数组, handler);
// InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求
ReflectiveFeign类的invoke()方法,第323行,代码的后半段,如下:
(MethodHandler)this.dispatch.get(method). invoke(args);
- this.dispatch:这是一个map,就是保存所有的MethodHandler的集合。参考创建InvocationHandler的位置:ReflectiveFeign类的newInstance()方法的第57行。
- this.dispatch.get(method):这里的method就是我们开发者写的feign接口中定义的方法的方法名!这段代码的意思就是从MethodHandler集合中拿到我们需要调用的那个方法。
- this.dispatch.get(method). invoke(args):这里的invoke就是调用的MethodHandler.invoke()!动态代理回调代理类,就这样完成了,oh my god,多么伟大的创举!
// 创建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处理
- BasicAuthRequestInterceptor:授权拦截器,主要是设置请求头的Authorization信息,这里是base64转码后的用户名和密码。
- FeignAcceptGzipEncodingInterceptor:编码类型拦截器,主要是设置请求头的Accept-Encoding信息,默认值{gzip, deflate}。
- FeignContextGzipEncodingInterceptor:压缩格式拦截器,该拦截器会判断请求头中Context-Length属性的值,是否大于请求内容的最大长度,如果超过最大长度2048,则设置请求头的Context-Encoding信息,默认值{gzip, deflate}。注意,这里的2048是可以设置的,可以在配置文件中进行配置:
feign.compression.request.enabled=true
feign.compression.request.min-request-size=2048
使用feign.Request把RequestTemplate包装成feign.Requestfeign.Request由5部分组成:
- method
- url
- headers
- body
- charset
- JDK自带的HttpUrlConnection
- Apache HttpClient
- OkHttpClient
// 具体实现有2个类Client.Default 和LoadBalancerFeignClientresponse = this.client.execute(request, this.options);
Client接口定义了execute()的接口,并且通过接口内部类实现了Client.execute()。HttpURLConnection connection = this.convertAndSend(request, options);
return this.convertResponse(connection).toBuilder(). request(request).build();
- 这里的Options定义了2个参数:
- connectTimeoutMillis:连接超时时间,默认10秒。
- readTimeoutMillis:读取数据超时时间,默认60秒。
- connectTimeoutMillis:连接超时时间,默认10秒。
也就是发送请求的另一种实现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();
以负载均衡的方式发送请求
- this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig))的具体实现在AbstractLoadBalancerAwareClient类中。
- executeWithLoaderBalancer()方法的实现也参考了响应式编程,通过LoadBalancerCommand提交请求,然后使用Observable接收响应信息。
AbstractLoadBalancerAwareClient实现了IClient接口,该接口定义了execute()方法,
- AbstractLoadBalancerAwareClient.this.execute()的具体实现有很多种:
- 【SpringCloud技术专题「Feign」从源码层面让你认识Feign工作流程和运作机制】OkHttpLoadBalancingClient
- RetryableOkHttpLoadBalancingClient
- RibbonLoadBalancingHttpClient
- RetryableRibbonLoadBalancingHttpClient
- 【SpringCloud技术专题「Feign」从源码层面让你认识Feign工作流程和运作机制】OkHttpLoadBalancingClient
第62行代码:
// 组装HttpUriRequestHttpUriRequest httpUriRequest = request.toRequest(requestConfig);
// 发送http请求HttpResponse httpResponse = ((HttpClient)this.delegate).execute(httpUriRequest);
// 使用RibbonApacheHttpResponse包装http响应信息return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
RibbonApacheHttpResponse由2部分组成:httpResponseuri
处理http相应
- 注册Feign客户端bean到IOC容器
- 查看Feign框架源代码,我们可以发现,FeignClientsRegistar的registerFeignClients()方法完成了feign相关bean的注册。
文章图片
- 第一步:基于JDK动态代理生成代理类。
- 第二步:根据接口类的注解声明规则,解析出底层MethodHandler
- 第三步:基于RequestBean动态生成request。
- 第四步:Encoder将bean包装成请求。
- 第五步:拦截器负责对请求和返回进行装饰处理。
- 第六步:日志记录。
- 第七步:基于重试器发送http请求,支持不同的http框架,默认使用的是HttpUrlConnection。
推荐阅读
- 你想学的黑客(攻击)技术全在这了,一篇打包带走!
- 靶机DC-1
- WEB安全新玩法 [11] 防范批量注册
- 测试开发之系统篇-Docker常用操作
- 如何在Django中从HTML创建PDF
- 使用jQuery Plugin Multiselect使用复选框实现可过滤的多选
- 如何在Windows上安装Ruby并建立基本的Hello World
- 用jQuery实现并排多重选择元素
- 如何使用jQuery自动使所有高度相同的div列