AI智能
改变未来

SpringCloud升级之路2020.0.x版-28.OpenFeign的生命周期-进行调用

本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent

接下来,我们开始分析 OpenFeign 同步环境下的生命周期的第二部分,使用 SynchronousMethodHandler 进行实际调用,其流程可以总结为:

  1. 调用代理类的方法实际调用的是前面一章中生成的
    InvocationHandler

    invoke

    方法。

  2. 默认实现是查询
    Map<Method, MethodHandler> methodToHandler

    找到对应的

    MethodHandler

    进行调用,对于同步 Feign,其实就是

    SynchronousMethodHandler
  3. 对于
    SynchronousMethodHandler

    :

  4. 使用前面一章分析创建的创建的请求模板工厂
    RequestTemplate.Factory

    ,创建请求模板

    RequestTemplate

  5. 读取 Options 配置
  6. 使用配置的 Retryer 创建新的 Retryer
  7. 执行请求并将响应反序列化 – executeAndDecode:
  8. 如果配置了 RequestInterceptor,则执行每一个 RequestInterceptor
  9. 将请求模板 RequestTemplate 转化为实际请求 Request
  10. 通过 Client 执行 Request
  11. 如果响应码是 2XX,使用 Decoder 解析 Response
  12. 如果响应码是 404,并且在前面一章介绍的配置中配置了 decode404 为 true, 使用 Decoder 解析 Response
  13. 对于其他响应码,使用 errorDecoder 解析,可以自己实现 errorDecoder 抛出 RetryableException 来走入重试逻辑
  14. 如果以上步骤抛出 IOException,直接封装成 RetryableException 抛出
  15. 如果第 4 步抛出 RetryableException,则使用第三步创建的 Retryer 判断是否重试,如果需要重试,则重新走第 4 步,否则,抛出异常。

给出这个流程后,我们来详细分析

OpenFeign的生命周期-进行调用源码分析

前面一章的最后,我们已经从源码中看到了这一章开头提到的流程的前两步,我们直接从第三步开始分析。

SynchronousMethodHandler

public Object invoke(Object[] argv) throws Throwable {//使用前面一章分析创建的创建的请求模板工厂 `RequestTemplate.Factory`,创建请求模板 `RequestTemplate`。RequestTemplate template = buildTemplateFromArgs.create(argv);//读取 Options 配置Options options = findOptions(argv);//使用配置的 Retryer 创建新的 RetryerRetryer retryer = this.retryer.clone();while (true) {try {//执行请求并将响应反序列化return executeAndDecode(template, options);} catch (RetryableException e) {//如果抛出 RetryableException,则使用 retryer 判断是否重试,如果需要重试,则继续请求即重试,否则,抛出异常。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;}}}

对于 executeAndDecode 其中的源码,为了兼容异步 OpenFeign 兼容 CompletableFuture 的特性,做了一些兼容性修改导致代码比较难以理解,由于我们这里不关心异步 Feign,所以我们将这块代码还原回来,在这里展示:

这个修改对应的 Issue 和 PullRequest 是:

  • Non-blocking I/O support
  • issue 361 – async feign variant supporting CompleteableFutures
Request targetRequest(RequestTemplate template) {//如果配置了 RequestInterceptor,则执行每一个 RequestInterceptorfor (RequestInterceptor interceptor : requestInterceptors) {interceptor.apply(template);}//将请求模板 RequestTemplate 转化为实际请求 Requestreturn target.apply(template);}Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {Request request = targetRequest(template);if (logLevel != Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);}Response response;long start = System.nanoTime();try {//通过 Client 执行 Requestresponse = client.execute(request, options);// ensure the request is set. TODO: remove in Feign 12response = 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);boolean shouldClose = true;try {if (logLevel != Logger.Level.NONE) {response =logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);}//如果响应码是 2XX,使用 Decoder 解析 Responseif (response.status() >= 200 && response.status() < 300) {if (void.class == metadata.returnType()) {return null;} else {Object result = decode(response);shouldClose = closeAfterDecode;return result;}} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {//如果响应码是 404,并且在前面一章介绍的配置中配置了 decode404 为 true, 使用 Decoder 解析 ResponseObject result = decode(response);shouldClose = closeAfterDecode;return result;} else {//对于其他响应码,使用 errorDecoder 解析,可以自己实现 errorDecoder 抛出 RetryableException 来走入重试逻辑throw errorDecoder.decode(metadata.configKey(), response);}} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);}//如果抛出 IOException,直接封装成 RetryableException 抛出throw errorReading(request, response, e);} finally {if (shouldClose) {ensureClosed(response.body());}}}static FeignException errorReading(Request request, Response response, IOException cause) {return new FeignException(response.status(),format("%s reading %s %s", cause.getMessage(), request.httpMethod(), request.url()),request,cause,request.body(),request.headers());}

这样,我们就分析完 OpenFeign 的生命周期

我们这一节详细介绍了 OpenFeign 进行调用的详细流程。接下来我们将开始介绍,spring-cloud-openfeign 里面,是如何定制 OpenFeign 的组件并粘合的。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » SpringCloud升级之路2020.0.x版-28.OpenFeign的生命周期-进行调用