微服务RESTful客户端Feign——源码解读篇

所谓一图胜千言,本篇文章先用Spring Cloud Feign类的脑图勾勒一个对Feign的简明印象。然后通过http请求的流程图和源码解析来解读Feign的功能。下图是对Spring Cloud Feign类的一个简单描述,先对整体的框架有个了解。

Spring Cloud Feign类解析

下图可以看出OpenFeign在微服务架构中的角色,即方便构建网络请求应用。

OpenFegin调用框架图

下图为调用OpenFeign的Http请求流程图

调用接口分析

(图片来自feign初識及源碼分析)

OpenFeign网络请求时序图

(图片来自feign初識及源碼分析)

OpenFeign的网络请求流程在源码里是如何的呢,下面将从类ReflectiveFeign开始展开

ReflectiveFeign$FeignInvocationHandler

private final Map<Method, MethodHandler> dispatch;

@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来处理, 即交给SynchronousMethodHandler来处理
  return dispatch.get(method).invoke(args);
}

dispatch是个容器类,通过调用MethodHandler.invoke(args)方法来处理,此处调用的是SynchronousMethodHandler的invoke()方法,该方法1生成传入实际参数值的RequestTemplate,2然后调用Target生成具体的Request对象(此处在生成Request对象前,会调用所有RequestInterceptor对RequestTemplate进行处理),3最后调用Client#execute()方法来发送网络请求

1. 生成RequestTemplate

SynchronousMethodHandler

@Override
public Object invoke(Object[] argv) throws Throwable {
  // 将函数实际参数添加到RequesstTemplate,使用类ReflectiveFeign$BuildTemplateByResolvingArgs的create()方法
  RequestTemplate template = buildTemplateFromArgs.create(argv);
  Retryer retryer = this.retryer.clone();
  while (true) {
    try {
      // 根据RequestTemplate生成request对象,然后交给Client实例发送网络请求
      return executeAndDecode(template);
    } catch (RetryableException e) {
      try {
        // 如果Response#status()为503,即重试请求,调用该方法重试,如果超过最大重试次数maxAttempts,抛出异常
        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;
    }
  }
}

ReflectiveFeign$BuildTemplateByResolvingArgs

@Override
public RequestTemplate create(Object[] argv) {
  RequestTemplate mutable = RequestTemplate.from(metadata.template());
  if (metadata.urlIndex() != null) {
    // 设置URL
    int urlIndex = metadata.urlIndex();
    checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
    mutable.target(String.valueOf(argv[urlIndex]));
  }
  Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
    // 遍历MethodMetaData中所有关于参数的索引及其对应名称的配置信息
  for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
    int i = entry.getKey();
    // entry.getKey就是参数的索引
    Object value = argv[entry.getKey()];
    if (value != null) { // Null values are skipped.
        // indexToExpander保存着将各种类型参数的值转换为string类型的Expander转换器
      if (indexToExpander.containsKey(i)) {
        // 将value值转换为String
        value = expandElements(indexToExpander.get(i), value);
      }
      for (String name : entry.getValue()) {
        varBuilder.put(name, value);
      }
    }
  }

  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()];
    // 设置queryMap参数
    Map<String, Object> queryMap = toQueryMap(value);
    template = addQueryMapQueryParameters(queryMap, template);
  }

  if (metadata.headerMapIndex() != null) {
    // 设置headerMap参数
    template =
        addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
  }

  return template;
}

2.调用Target生成具体的Request对象

SynchronousMethodHandler

Object executeAndDecode(RequestTemplate template) throws Throwable {
    // 调用targetRequest()方法,构建Request
    Request request = targetRequest(template);

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

    Response response;
    long start = System.nanoTime();
    try {
      // 调用Client.execute()方法发送网络请求
      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());
      }
    }
  }

SynchronousMethodHandler

  Request targetRequest(RequestTemplate template) {
    // 调用所有的请求拦截器,为每个请求添加固定的header信息
    for (RequestInterceptor interceptor : requestInterceptors) {
      interceptor.apply(template);
    }
    return target.apply(template);
  }

Target

    // 创建Request
    @Override
    public Request apply(RequestTemplate input) {
      if (input.url().indexOf("http") != 0) {
        input.target(url());
      }
      return input.request();
    }

3. 调用Client#execute()方法来发送网络请求

此处以OkHttp的方法举例,OkHttpClient

@Override
  public feign.Response execute(feign.Request input, feign.Request.Options options)
      throws IOException {
    okhttp3.OkHttpClient requestScoped;
    if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
        || delegate.readTimeoutMillis() != options.readTimeoutMillis()) {
      requestScoped = delegate.newBuilder()
          .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
          .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
          .followRedirects(options.isFollowRedirects())
          .build();
    } else {
      requestScoped = delegate;
    }
    // 将feign.Request转换为OkHttp的Request对象
    Request request = toOkHttpRequest(input);
    // 使用OkHttp的同步操作发送网络请求
    Response response = requestScoped.newCall(request).execute();
    // 将OkHttp的Response转换为feign.Response
    return toFeignResponse(response, input).toBuilder().request(input).build();
  }

推荐阅读

Spring-cloud-openFeign source depth analysis

Spring Cloud Feign的loadbalancerkey扩展

Spring Cloud Issue - public FeignLoadBalancer.RibbonRequest and FeignLoadBalancer.RibbonResponse to the user to customize their needs

Spring Cloud Netflix: Load Balancer with Ribbon/Feign

Spring Cloud Issue - Unable to build or run all tests successfully on windows

feign初識及源碼分析

spring-cloud-openfeign原理分析

猜你喜欢

转载自blog.csdn.net/u010145219/article/details/94437363