feign源码解析 - 运行时

基于spring-cloud-openfeign-core-2.2.5.RELEASE

1. 前言

在前文 feign源码解析_初始化中,我们探讨了feign如何基于JDK Proxy特性,构建出相应的@FeignClient接口实现,供使用者取用。

本文我们将以此为基础,探讨在上述@FeignClient实例基础上,feign是如何基于使用者的接口调用请求,发起相应的http调用,返回相应结果的。

2. 入口ReflectiveFeign.FeignInvocationHandler

如前文 feign源码解析_初始化已经论述的,feign基于JDK Proxy实现的@FeignClient接口实现类。基于此我们找到典型的InvocationHandler实现类,在本文场景下就是ReflectiveFeign.FeignInvocationHandler

	// FeignInvocationHandler.invoke(...)
    @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();
      }

	  // 最终调用的是 MethodHandler.invoke(Object[] argv), 默认实现类为 SynchronousMethodHandler
      return dispatch.get(method).invoke(args);
    }

3. 逻辑主体 SynchronousMethodHandler.invoke(Object[] argv)

上述FeignInvocationHandler的实现,只是简单地将逻辑控制权调度给了MethodHandler实现,这里是SynchronousMethodHandler

  // ================================== SynchronousMethodHandler.invoke(...)
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    // 基于入参,构建出http request template。
    //	1. @FeignClient修饰的接口类, 由 方法参数 生成出 RequestTemplate实例。   
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 找出可能的Options配置项。使用者可以在@FeignClient接口方法中定义相应类型参数, 达到灵活配置目的. 
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
      	// 主体逻辑处
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        // 如果发生retry异常
        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;
      }
    }
  }

  // ================================== SynchronousMethodHandler.executeAndDecode(...)
  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    // 依次回调外界自定义的 RequestInterceptor 实现
    //	2. 由 RequestTemplate实例 生成 Request实例  
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    // 记录请求耗时   
    long start = System.nanoTime();
    try {
      // 3. 使用Client实现类(LoadBalancerFeignClient.java这个对外门面), 发起http请求, 获取响应。
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      // 注意这里捕获的异常类型, 所以如果出现类似错误"com.netflix.client.ClientException: Load balancer does not have available server for client: xx-service" 
        //	那流程就不会往下走了, 也自然不能触发 ErrorDecoder 扩展机制; 将直接跳进 Fallback    
      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);

    // 调用 Decoder接口实现类, 默认为null 
    if (decoder != null)
      return decoder.decode(response, metadata.returnType());

	// 回调 AsyncResponseHandler类方法(详解参见下方专门小节)
	// 4. 解析结果
    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");
	  // 5. 返回结果
      return resultFuture.join();
    } catch (CompletionException e) {
      // 上面的 CompletableFuture 执行失败抛出异常, 逻辑将跳转到这里
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
  }

4. 响应解析AsyncResponseHandler.handleResponse(...)

feign中使用了专门的内部类 AsyncResponseHandler来负责解析本次请求的响应Response。

  // AsyncResponseHandler.handleResponse(...)
  void handleResponse(CompletableFuture<Object> resultFuture,
                      String configKey,
                      Response response,
                      Type returnType,
                      long elapsedTime) {
    // copied fairly liberally from SynchronousMethodHandler
    boolean shouldClose = true;

    try {
      if (logLevel != Level.NONE) {
        response = logger.logAndRebufferResponse(configKey, logLevel, response,
            elapsedTime);
      }
      // 1. 如果@FeignClient接口方法定义返回值类型就是Response
      if (Response.class == returnType) {
        if (response.body() == null) {
          resultFuture.complete(response);
        } else if (response.body().length() == null
            || response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
          shouldClose = false;
          resultFuture.complete(response);
        } else {
          // Ensure the response body is disconnected
          final byte[] bodyData = Util.toByteArray(response.body().asInputStream());
          resultFuture.complete(response.toBuilder().body(bodyData).build());
        }
      } else if (response.status() >= 200 && response.status() < 300) {
        // 2. 如果http response code 为 2xx
        if (isVoidType(returnType)) {
          resultFuture.complete(null);
        } else {
          final Object result = decode(response, returnType);
          shouldClose = closeAfterDecode;
          resultFuture.complete(result);
        }
      } else if (decode404 && response.status() == 404 && !isVoidType(returnType)) {
        // 3. 如果http response code 为 404, 并且配置了feign会处理404错误. 
        final Object result = decode(response, returnType);
        shouldClose = closeAfterDecode;
        resultFuture.complete(result);
      } else {
        // 4. 认定http请求调用, 发生异常. 
        resultFuture.completeExceptionally(errorDecoder.decode(configKey, response));
      }
    } catch (final IOException e) {
      if (logLevel != Level.NONE) {
        logger.logIOException(configKey, logLevel, e, elapsedTime);
      }
      resultFuture.completeExceptionally(errorReading(response.request(), response, e));
    } catch (final Exception e) {
      resultFuture.completeExceptionally(e);
    } finally {
      if (shouldClose) {
        ensureClosed(response.body());
      }
    }

  }

5. 整体时序图

以上处理流程,串起来就是下面这副 feign运行时处理时序图了。
feign_runtime

6. 参考

  1. feign源码解析_初始化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值