feign分析之Client,Retryer

235 阅读3分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

简介

02简介1.png

  1. 基于JDK动态代理生成代理类
  2. 根据接口的注解规则,解析出MethodHandler
  3. Encode将RequestBean生成RequestTemplate
  4. 经过多个拦截器处理RequestTemplate
  5. 根据日志级别打印日志
  6. 发送http请求
  7. 根据响应决定是否进行重试
  8. 根据日志级别打印日志
  9. 将response Decode为接口声明的返回类型

程序入口

T t = Feign.builder().build().target(Target<T> target);
t.xxxx();

主流程

Feign.Builder

 public Feign build() {
   // Capability接口对原始对象进行包装
      Client client = Capability.enrich(this.client, capabilities);
      Retryer retryer = Capability.enrich(this.retryer, capabilities);
      List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
          .map(ri -> Capability.enrich(ri, capabilities))
          .collect(Collectors.toList());
      Logger logger = Capability.enrich(this.logger, capabilities);
      Contract contract = Capability.enrich(this.contract, capabilities);
      Options options = Capability.enrich(this.options, capabilities);
      Encoder encoder = Capability.enrich(this.encoder, capabilities);
      Decoder decoder = Capability.enrich(this.decoder, capabilities);
      InvocationHandlerFactory invocationHandlerFactory =
          Capability.enrich(this.invocationHandlerFactory, capabilities);
      QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }

Capability 接口对 Client,Retryer,RequestInterceptor,Logger,Contract,Options,Encoder,Decoder,InvocationHandlerFactory,QueryMapEncoder进行包装; 例如: HystrixCapability装饰类对Contract,InvocationHandlerFactory进行包装;

static <E> E enrich(E componentToEnrich, List<Capability> capabilities) {
    return capabilities.stream()
        // invoke each individual capability and feed the result to the next one.
        // This is equivalent to:
        // Capability cap1 = ...;
        // Capability cap2 = ...;
        // Capability cap2 = ...;
        // Contract contract = ...;
        // Contract contract1 = cap1.enrich(contract);
        // Contract contract2 = cap2.enrich(contract1);
        // Contract contract3 = cap3.enrich(contract2);
        // or in a more compact version
        // Contract enrichedContract = cap3.enrich(cap2.enrich(cap1.enrich(contract)));
        .reduce(
            componentToEnrich,
            (component, capability) -> invoke(component, capability),
            (component, enrichedComponent) -> enrichedComponent);
  }

  static <E> E invoke(E target, Capability capability) {
    return Arrays.stream(capability.getClass().getMethods())
        .filter(method -> method.getName().equals("enrich"))
        .filter(method -> method.getReturnType().isInstance(target))
        .findFirst()
        .map(method -> {
          try {
            return (E) method.invoke(capability, target);
          } catch (IllegalAccessException | IllegalArgumentException
              | InvocationTargetException e) {
            throw new RuntimeException("Unable to enrich " + target, e);
          }
        })
        .orElse(target);
  }

ReflectiveFeign

public <T> T newInstance(Target<T> target) {
    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;
  }

ReflectiveFeign.ParseHandlersByName

public Map<String, MethodHandler> apply(Target target) {
      List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate =
              new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else if (md.bodyIndex() != null || md.alwaysEncodeBody()) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
        }
        if (md.isIgnored()) {
          result.put(md.configKey(), args -> {
            throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
          });
        } else {
          result.put(md.configKey(),
              factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
        }
      }
      return result;
    }
  }

组件介绍

01 Client

  1. 提交Http请求,实现类需要是线程安全的

  2. 对Http的抽象,实现类有 HttpClient,JDK自带的Http, OKHttpClient等

  3. 设计思想: 问题:由于存在多种框架来实现Http请求,如何对多种框架来做兼容,或者说如何对多种框架来做集成? 对Http请求链接向上抽象, 定义自己的接口,自己的接口在自己的框架内交互, 交互过程中,框架内其他类对于什么实现这个借口并不关心。 这里使用适配器模式中-对象结构型模式 02client1.png

    例如:ApacheHttpClient包集成,想要把Apache的HttpClient 转换(适配)为Feign的Client, 则需要有个适配者 ApacheHttpClient来做这个工作, ApacheHttpClient 聚合了HttpClient,实现了Client接口。

02client2.png

  1. 参数Request,是对请求的封装,包含有: method(GET/POST…),url,headers,body,charset
  2. 响应值Response,是对请求响应的封装,包含有: status(响应状态码), reason,headers,body,request;其中 包含内部类Builder,方便构建Response。
  3. 参数Options,配置信息封装,包含:connectTimeout,connectTimeoutUnit,readTimeout,readTimeoutUnit,followRedirects(是否重定向)
  4. 针对于Client的包装类,MeteredClient,包装过程查看主流程讲解

02 Retryer

重试策略

每次调用都克隆到{@link Client#execute(Request, feign.Request.Options)}。

实现可以保持状态以确定重试操作是否应该继续。

  1. Retryer类图,Retryer包含两种, Retryer.Default是进行重试默认频率为 重试次数 5,过期时间1分钟, 匿名内部类 Retryer.NEVER_RETRY
  2. continueOrPropagate(RetryableException e) 方法,如果允许重试, 直接返回,否则传播异常。
  3. Retryer clone() 克隆方法,保持重试的状态, 通知下次重试的时候为第几次重试