通过断点获取SpringCloud的OpenFegin调用失败的原因

之前有参与过一个缴费相关的SpringCloud项目,开发的过程中,也踩过不少的坑。总会遇到几次,fegin调用失败直接走了fallback。但是只是看fallback,根本看不出什么有用的信息。很多时候只能再去看看哪里没有对应上,特别耽误时间。然后就自己追源码找到了一个可行的方案。

本地测试的时候,遇到fegin调用失败,能打断点发现真正的错误原因么?

当然可以!

但是首先我们需要先简单的了解feign

1.Feign的简单基础知识

FeginClient注解的方法,会通过Proxy代理生成代理类执行。即通过Proxy的newProxyInstance方法执行得到了代理类,newProxyInstance方法有三个参数ClassLoader、Class<?>[]和InvocationHandler。生成的代理类主要执行的方法就是通过代理的执行器InvocationHandlerinvoke方法来执行的。

对于想了解更多关于Fegin原理的小伙伴,可以先去看看亦山的文章,这里就不详细说明原理了。

Spring Cloud-Feign设计原理_我的程序人生(亦山札记)-CSDN博客

在OpenFegin中,执行器InvocationHandler对应的其中一个实现类是HystrixInvocationHandler(io.github.openfeign.feign-hystrix)

final class HystrixInvocationHandler implements InvocationHandler {


/*只粘贴了核心方法,详情参数看源码*/
  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    // early exit if the invoked method is from java.lang.Object
    // code is the same as ReflectiveFeign.FeignInvocationHandler
    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();
    }

    HystrixCommand<Object> hystrixCommand =
        new HystrixCommand<Object>(setterMethodMap.get(method)) {
          @Override
          protected Object run() throws Exception {
            try {
                /* 
                 * hystrixCommand.execute()执行后会调用该方法,此时会通过method获取对应的
                 * MethodHandler,并执行invoke方法
                 */
              return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
            } catch (Exception e) {
              throw e;
            } catch (Throwable t) {
              throw (Error) t;
            }
          }

          @Override
          protected Object getFallback() {
            if (fallbackFactory == null) {
              return super.getFallback();
            }
            try {
              Object fallback = fallbackFactory.create(getExecutionException());
              Object result = fallbackMethodMap.get(method).invoke(fallback, args);
              if (isReturnsHystrixCommand(method)) {
                return ((HystrixCommand) result).execute();
              } else if (isReturnsObservable(method)) {
                // Create a cold Observable
                return ((Observable) result).toBlocking().first();
              } else if (isReturnsSingle(method)) {
                // Create a cold Observable as a Single
                return ((Single) result).toObservable().toBlocking().first();
              } else if (isReturnsCompletable(method)) {
                ((Completable) result).await();
                return null;
              } else {
                return result;
              }
            } catch (IllegalAccessException e) {
              // shouldn't happen as method is public due to being an interface
              throw new AssertionError(e);
            } catch (InvocationTargetException e) {
              // Exceptions on fallback are tossed by Hystrix
              throw new AssertionError(e.getCause());
            }
          }
        };

    if (Util.isDefault(method)) {
      return hystrixCommand.execute();
    } else if (isReturnsHystrixCommand(method)) {
      return hystrixCommand;
    } else if (isReturnsObservable(method)) {
      // Create a cold Observable
      return hystrixCommand.toObservable();
    } else if (isReturnsSingle(method)) {
      // Create a cold Observable as a Single
      return hystrixCommand.toObservable().toSingle();
    } else if (isReturnsCompletable(method)) {
      return hystrixCommand.toObservable().toCompletable();
    }
   /*正常执行此方法*/
    return hystrixCommand.execute();
  }

}

通过 HystrixInvocationHandler.this.dispatch.get(method)方法获取到了对应的MethodHandler, 其中对应的实现类为SynchronousMethodHandler.

final class SynchronousMethodHandler implements MethodHandler {

@Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template);
      } 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) throws Throwable {
    Request request = targetRequest(template);

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

    Response response;
    long start = System.nanoTime();
    try {
    /*断点打下面一行直接获取对应的response就行*/
      response = client.execute(request, options);
    } 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);
      }
      if (Response.class == metadata.returnType()) {
        if (response.body() == null) {
          return response;
        }
        if (response.body().length() == null ||
            response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
          shouldClose = false;
          return response;
        }
        // Ensure the response body is disconnected
        byte[] bodyData = Util.toByteArray(response.body().asInputStream());
        return response.toBuilder().body(bodyData).build();
      }
      if (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()) {
        Object result = decode(response);
        shouldClose = closeAfterDecode;
        return result;
      } else {
        throw errorDecoder.decode(metadata.configKey(), response);
      }
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
      }
      throw errorReading(request, response, e);
    } finally {
      if (shouldClose) {
        ensureClosed(response.body());
      }
    }
  }

Object decode(Response response) throws Throwable {
    try {
      return decoder.decode(response, metadata.returnType());
    } catch (FeignException e) {
      throw e;
    } catch (RuntimeException e) {
      throw new DecodeException(e.getMessage(), e);
    }
  }

}

断点可以打到返回response的后面。通过response的返回就可以获取fegin调用之后的状态了。

2.断点实操

通过response不同状态的返回,以及response对象,我们能知道更多的信息。

(1) 正常200的返回

(2)错误404的返回

(3)错误500的返回

500的错误还是比较特殊的,只是这么看的话,也很难知道具体的报错信息,这个时候就可以在另外一个地方打断点来获取详细的报错信息了。其实就是HystrixInvocationHandler的异常抛出的位置(如下图)。

以上就是判断Fegin报错并断点调试的方法。方法知道了,那么有的小伙伴就想问,实际使用的时候如何快速找到SynchronousMethodHandlerHystrixInvocationHandler呢?毕竟实践才是检验真理的唯一标准!

3.我在哪里?

首先确认项目中已经引用了OpenFegin的依赖,然后就可以在依赖中直接找到对应的类,话不多说直接上图。

然后在Libraries里面找io.github.openfeign:feign-core包,点开后就可以找到SynchronousMethodHandler。

在io.github.openfeign:feign-core包则可以找到HystrixInvocationHandler

当然也可以在github上看源码 

https://github.com/OpenFeign/feign

4.结语

就跟搞笑版《XX宝典》一样,不必看上面的也可练此神功。

根本没必要这么麻烦 ,如果想把这样的异常直接抛出或者打印出来,只需要把fallback类实现FallbackFactory接口,并且将默认的create方法中将Throwable抛出或者打印即可。。很多时候象生产环境等,不开放对应端口,想在服务器上打断点并不容易,并且会影响其他用户的使用。所以还是这个方法更有效。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值