Spring Cloud OpenFeign执行流程解析(待完善)

Feign是向远程服务器发送请求的客户端,类似于RestTemplate,但是使用声明式的方式,实际使用的时候请求会根据声明的方法签名,注解这些动态构造请求。下面主要是分析Spring Cloud集成的Openfeign的一系列执行流程。

一个最基本的使用情况如下:

首先在配置类上启用Feign:

@EnableFeignClients
public class SpringCloudFeignDemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(SpringCloudFeignDemoApplication.class, args);
  }
}

然后创建一个接口,声明为FegnClient,这样Spring启动的时候会为它注册一个bean。

@FeignClient(name = "client", url = "http://localhost:8080")
@Component  // There shouldn't be this annotation here, but this need to be here because of IDEA bug
public interface FeignClientX {

  @GetMapping("print")
  public void print(@SpringQueryMap Param param);
}

对于每一个FeignClient实例, Spring都会创建一个代理类ReflectiveFeign.FeignInvocationHandler代理对象,调用FeignClient方法时会被拦截处理,处理代码如下:

Class: ReflectiveFeign.FeignInvocationHandler

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();
      }

      return dispatch.get(method).invoke(args);
    }

如果是equals()方法,则判断代理对象是否是同一个,如果是hashCode()和toString()方法,则调用代理对象的hashCode()和toString()方法,否则就查找对应方法并调用。

每个FeignInvocationHandler代理对象都有一个dispatch属性,其原型为:

private final Map<Method, MethodHandler> dispatch;

是在生成FeignClient代理对象的时候会处理每个方法上的注解,生成一个处理对象SynchronousMethodHandler放到此Map里,调用时根据方法签名对应的key来获取到具体的SynchronousMethodHandler对象。

而每个SynchronousMethodHandler又封装了发起请求的Client对象,重试处理器等:

  private final MethodMetadata metadata;
  private final Target<?> target;
  private final Client client;
  private final Retryer retryer;
  private final List<RequestInterceptor> requestInterceptors;
  private final Logger logger;
  private final Logger.Level logLevel;
  private final RequestTemplate.Factory buildTemplateFromArgs;
  private final Options options;
  private final Decoder decoder;
  private final ErrorDecoder errorDecoder;
  private final boolean decode404;
  private final boolean closeAfterDecode;
  private final ExceptionPropagationPolicy propagationPolicy;

当Feign代理对象通过dispatch属性找到对应的SynchronousMethodHandler对象的后,就会调用它的invoke方法,如果出错,并且配置了Retryer则还会重试,实现如下:

  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 如果方法参数中有Options对象,则使用参数中的Options,否则使用默认的
    Options options = findOptions(argv);
    // 复制一个Retryer
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
          // 检查是否到达最大重试次数,如果超过则throw异常跳出循环,否则继续
          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;
      }
    }
  }

第一步根据MethodMetadata里保存的RequestTemplate以及传进来的参数argv构建RequestTemplate:

    public RequestTemplate create(Object[] argv) {
      
      // 依据MethodMetadata里面保存的RequestTemplate生成一个新的RequestTemplate。
      RequestTemplate mutable = RequestTemplate.from(metadata.template());
      
      // metadata.urlIndex() != null判断方法参数里是否有URI类型参数, 如果有则更新Url为URI的地址
      if (metadata.urlIndex() != null) {
        int urlIndex = metadata.urlIndex();
        checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
        mutable.target(String.valueOf(argv[urlIndex]));
      }
      
      // 根据参数索引找到参数名称,然后从argv数组中取值,最后把名和值对应放入varBuilder中
      Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
      for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
        int i = entry.getKey();
        Object value = argv[entry.getKey()];
        if (value != null) { // Null values are skipped.
          if (indexToExpander.containsKey(i)) {
            value = expandElements(indexToExpander.get(i), value);
          }
          for (String name : entry.getValue()) {
            varBuilder.put(name, value);
          }
        }
      }

	  // 解析URL, header, body等中的占位符
      RequestTemplate template = resolve(argv, mutable, varBuilder);
      if (metadata.queryMapIndex() != null) {
        // add query map parameters after initial resolve so that they take
        // precedence over any predefined values
        Object value = argv[metadata.queryMapIndex()];
        Map<String, Object> queryMap = toQueryMap(value);
        template = addQueryMapQueryParameters(queryMap, template);
      }

      if (metadata.headerMapIndex() != null) {
        template =
            addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
      }

      return template;
    }

下面开始执行请求并处理返回类型

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
  // 应用RequestTemplate中的所有interceptor,并且生成Request
  Request request = targetRequest(template);

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

  Response response;
  long start = System.nanoTime();
  try {
    // 如果没有配置Ribbon, 默认的Client是Default类型,使用HttpURLConnection发送请求与返回。
    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);
    }
    
    // 方法返回类型是Response类型则直接返回response
    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 {
      // 调用ErrorDecoder
      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());
    }
  }
}

下面插一个时序图方便理解:
在这里插入图片描述

Guess you like

Origin blog.csdn.net/adolph09/article/details/105883830