SpringCloud.Honxton 版本 OpenFeign原理上篇

SpringCloud.Honxton 版本 OpenFeign原理

前置说明

好久没写博客了, 突然想起自己的springcloud系列文章就讲了配置中心,注册中心,负载均衡和熔断。那么今天就来分析一下openfegin 的原理,后续会分析gateway的原理。

我个人的习惯,在使用springcloud 的组件时,会先去用一下原生依赖的使用方法,因为在springboot的自动装配下让使用变得简单,但使用也更加机械化,导致我们会无法看透到底在用一个什么东西。 所以我们先单独分析一下openfeign的使用以及其本身的原理,然后再来分析springcloud如何进行的整合(下篇)。

openfegin是由neflix公司开源出来,再迭代了数个版本之后彻底改名为openfeign,在被springcloud整合之后也是十分的受大家的欢迎。

如何使用

public interface HelloOpenfeign {

       @RequestLine("GET /feign?feign={feign}")
    @Headers("Content-Type: application/x-www-form-urlencoded")
    String feign(@Param("feign") String feign);

    @RequestLine("POST /feignPost")
    @Headers("Content-Type: application/x-www-form-urlencoded")
    String feignPost(@Param("feign") String feign);

    @RequestLine("POST /feignJson")
    @Headers("Content-Type: application/json")
    String feignForm(@Param("feign") String feign);

    public static void main(String[] args) {
        HelloOpenfeign client = Feign.builder().target(HelloOpenfeign.class, "http://localhost:12001");
        String result = client.feign("YourBatman");
        System.out.println(result);
    }
}


哎,是不是没有想到,使用方法竟然如此简单, 其实就是解析接口所有的方法,根据你配置的注解完成http参数的拼装,然后给目的地的ip:port就可以了。最后获取动态代理对象,那么就像调用本地方法一样调用http请求了。

openfeign原理

前面其实已经讲了内部原理的大致流程, 也就是解析接口中的方法, 然后生成代理对象; 在真正调用时,解析传递的参数,然后封装request,最后进行真正远程调用, 最后对返回的结果解析封装成接口方法的返回类。

下面开始结合源码进行关键点的分析。

入口方法就是 feign.Feign.Builder#target(java.lang.Class, java.lang.String)

public static class Builder {
... 省略其他方法

 public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }

    public Feign build() {
    // 用于创建真正执行的handler 的工厂
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy);
    // 接口方法解析器          
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
              // build模式创建的Feign接口的实现类
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }


}

然后来看一下ReflectiveFeign

public class ReflectiveFeign extends Feign {

@Override
  public <T> T newInstance(Target<T> target) {
  // 用 接口方法解析器解析所有的方法, 产生MethodHandler抽象
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
   //  所有的方法处理器
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    // 不是远程调用的接口方法处理器
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
	//遍历所有的方法
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    // 动态代理执行器
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 代理对象生成
    T proxy = (T)  Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy); // 绑定反射(其实不是,但是可以理解为反射)的实例
    }
    return proxy;
  }


}

然后简单分析一下参数解析的过程, 主要是 ParseHandlersByName这个类


 public Map<String, MethodHandler> apply(Target key) {
      List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
         // 一般的post,有表单数据的生成器
          buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else {
        // 一般的get, 没有表单数据的生成器
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
        }
        // 这里是关键的创建 SynchronousMethodHandler
        result.put(md.configKey(),
            factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }

看到这其实openfeign的初始化流程已经很明确, 然后是动态代理的执行过程的分析 具体见FeignInvocationHandler

static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }
@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();
      }
		// 关键的方法
      return dispatch.get(method).invoke(args);
    }
... 省略多余代码
}


final class SynchronousMethodHandler implements MethodHandler {

@Override
  public Object invoke(Object[] argv) throws Throwable {
  //根据输入的参数, 生成对应的请求模板
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    // 重试策略
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
      // 真正的执行远程调用和解析
        return executeAndDecode(template, options);
      } 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, 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 {
    // 这里又是经典的策略模式, 也是springcloud集成负载均衡的关键点
    // 这里是进行远程调用
      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.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);
      }
  }
  ... 省略多余代码
}


// 默认的client实现
class Default implements Client {
    private final SSLSocketFactory sslContextFactory;
    private final HostnameVerifier hostnameVerifier;

    @Override
    public Response execute(Request request, Options options) throws IOException {		// 是最简单的 jdk提供的http连接器
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }
}

基本上feigin的启动和执行过程都分析到了, 只有一些参数解析的和响应对象解析的没有分析(这些细节不影响我们宏观把控feign的流程).

最后给上一副feign 的分层架构设计图.
在这里插入图片描述

总结

整体来说 feign 的使用非常的便捷, 但是不爽的地方在于就我们需要自己调用Feign.builder().target来生成代理对象.不过好在有spring来帮我们做了一些琐碎的事情来避免我们针对每个接口都调用target方法, 就像Aop的整合一样, 省去了我们自己生产动态代理对象.
下一篇我就会讲述springcloud下openfeign的使用,整合的原理以及自动配置的原理.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章