『 OpenFeign』原理篇

575 阅读9分钟

前言

上一节,我们学习了 Feign 的相关配置与使用。这节继续学习 Feign 的相关源码。

启动加载

  我们知道,在使用 Feign 时,需要在启动类上使用 @EnableFeignClients 注解。

  Open Feign 的奥秘就藏在 @EnableFeignClients 中:

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

   /** basePackages 别名 */ 
   String[] value() default {};

   /** 扫描哪些包路径 */
   String[] basePackages() default {};

   /** 扫描某类所在的标签 */
   Class<?>[] basePackageClasses() default {};

   /** Feign 的全局配置类 */
   Class<?>[] defaultConfiguration() default {};

   /** 扫描哪些类 */
   Class<?>[] clients() default {};

}

  @EnableFeignClients 注解包含了一些属性,例如包路径,全局配置类等等,但其中最重要的还是 @Import(FeignClientsRegistrar.class) 注解。

  之前通过 《深入学习 Spring Boot》系列我们知道,Spring Boot 启动时,会执行被 @Import(XXX.class) 导入的ImportBeanDefinitionRegistrar配置类。

  所以下面,我们直接去看 FeignClientsRegistrar 类。

FeignClientsRegistrar

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

   private ResourceLoader resourceLoader;

   private Environment environment;

   // ...
   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata,
         BeanDefinitionRegistry registry) {
      // 注册全局配置
      registerDefaultConfiguration(metadata, registry);
      // 注册FeignClient
      registerFeignClients(metadata, registry);
   }

   // ...
}

  核心方法就是这个 registerBeanDefinitions 了,内部分别调用了 registerDefaultConfigurationregisterFeignClients,用于加载配置与加载 Feign Client。

registerDefaultConfiguration

  这个里面基本上就是去扫描默认的配置。

private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
        // 获取 EnableFeignClients 的属性值
   Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
   // 处理 name 与 Feign配置 的映射关系
   if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
      String name;
      if (metadata.hasEnclosingClass()) {
         name = "default." + metadata.getEnclosingClassName();
      }
      else {
         name = "default." + metadata.getClassName();
      }
      // 由于这里是默认配置,所以加上了 default 前端。
      // 实际 name = default.XXX.XXXX.XXX
      registerClientConfiguration(registry, name,
            defaultAttrs.get("defaultConfiguration"));
   }
}

// 这个方法在注册 Feign Client 的配置类时,还会用到。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
      Object configuration) {
   BeanDefinitionBuilder builder = BeanDefinitionBuilder
         .genericBeanDefinition(FeignClientSpecification.class);
   builder.addConstructorArgValue(name);
   builder.addConstructorArgValue(configuration);
   // 这个最后在注册 BD 时,又把 name 加了一层。
   // feign-name.配置类地址.FeignClientSpecification#getSimpleName
   registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());
}

  

registerFeignClients

public void registerFeignClients(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
   LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
   // ... 扫描配置的 OpenFeign 包或者类,到 candidateComponents 中。
   for (BeanDefinition candidateComponent : candidateComponents) {
      if (candidateComponent instanceof AnnotatedBeanDefinition) {
         // verify annotated class is an interface
         AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) c
         AnnotationMetadata annotationMetadata = beanDefinition.getMetadata()
         Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");
         Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName(
         String name = getClientName(attributes);
         // 注册 FeignClient 上的配置类。和上面的一样。
         registerClientConfiguration(registry, name,attributes.get("configuration"));
         // 注册 FeignClient 本身
         registerFeignClient(registry, annotationMetadata, attributes);
      }
   }
}

// 注册 Fegin BD 到 Spring 上下文中,留待后续进行 Bean 初始化。
private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
   // 获取 FeginClient 的 Class 对象。
   String className = annotationMetadata.getClassName();
   Class clazz = ClassUtils.resolveClassName(className, null);
   // 获取 Feign 的一些基础配置项
   ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactor? (ConfigurableBeanFactory) registry : null;
   String contextId = getContextId(beanFactory, attributes);
   String name = getName(attributes);
   // Spring Boot 提供的 FactoryBean 机制,可以对 Bean 做增强。
   // 《深入学习 Spring Boot》 系列中有提到过,后面也会重点分析该类。
   FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
   factoryBean.setBeanFactory(beanFactory);
   factoryBean.setName(name);
   factoryBean.setContextId(contextId);
   factoryBean.setType(clazz);
   // 构建 FeignClient 的 BeanDefinitionBuilder
   BeanDefinitionBuilder definition = BeanDefinitionBuilder
         .genericBeanDefinition(clazz, () -> {
            // 一些 FeignClient 的基础配置。
            factoryBean.setUrl(getUrl(beanFactory, attributes));
            factoryBean.setPath(getPath(beanFactory, attributes));
            factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
            // 这个我们很熟悉,Feign 的 Fallback。
            Object fallback = attributes.get("fallback");
            if (fallback != null) {
               factoryBean.setFallback(fallback instanceof Class
                     ? (Class<?>) fallback
                     : ClassUtils.resolveClassName(fallback.toString(), null)
            }
            Object fallbackFactory = attributes.get("fallbackFactory");
            if (fallbackFactory != null) {
               factoryBean.setFallbackFactory(fallbackFactory instanceof Class
                     ? (Class<?>) fallbackFactory
                     : ClassUtils.resolveClassName(fallbackFactory.toString()
                           null));
            }
            // 获取 Feign 的增强代理的方法.
            // 后续我们使用注入 FeignClient 实例时,Spring 会通过这个方法为我们注入 FeignClient 的代理类。
            return factoryBean.getObject();
         });
   // 设置一些基本参数
   definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
   definition.setLazyInit(true);
   validate(attributes);
   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
   beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
   beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
   // has a default, won't be null
   boolean primary = (Boolean) attributes.get("primary");
   beanDefinition.setPrimary(primary);
   String[] qualifiers = getQualifiers(attributes);
   // 设置默认别名
   if (ObjectUtils.isEmpty(qualifiers)) {
      qualifiers = new String[] { contextId + "FeignClient" };
   }
   // 注册 BD
   BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,qualifiers);
   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

  那么至此,Spring Boot 就帮助我们,将 Feign 配置类以及 Feign Client BD 扫描到 Spring Boot 中了,留待后一步初始化。

  

代理增强的实现

FeignClientFactoryBean

public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,ApplicationContextAware, BeanFactoryAware {

   // ...

   @Override
   public Object getObject() {
      return getTarget();
   }

   <T> T getTarget() {
      // 获取 FeignContext 获取 FeignClient 的上下文,每个 Client 一个 Context,由此实现配置隔离。
      FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class):applicationContext.getBean(FeignContext.class);
      Feign.Builder builder = feign(context);
      // 通常我们走这一分支
      if (!StringUtils.hasText(url)) {
         if (!name.startsWith("http")) {
            url = "http://" + name;
         } else {
            url = name;
         }
         url += cleanPath();
         return (T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url));
      }
      // ...
   }

   protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
         HardCodedTarget<T> target) {
      // 从容器中获取 Client。Client 就是 HTTP 请求的执行器了。
      // 一般情况下,这里选择到的 Client 实现是 LoadBalancerFeignClient ,其中包含了负载均衡的能力。
      Client client = getOptional(context, Client.class);
      if (client != null) {
         builder.client(client);
         // 当我们选用不同的断路器实现时,这里获取获取不同的实现。
         // 目前最常用的还是 Hystrix。
         Targeter targeter = get(context, Targeter.class);
         // 第三方可以在此做一个增强实现,例如 Hystrix 在此设置了 fallback。
         return targeter.target(this, builder, context, target);
      }

      // ...
   }
}

Feign

public abstract class Feign {

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

    public Feign build() {
      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);

      // 此类是将来生成 Feign 代理类的代理功能工厂类。
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
      // 此类将来用于扫面 Fegin 中包含的方法与生成对应方法的增强方法。
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
}

public class ReflectiveFeign extends Feign {

  public <T> T newInstance(Target<T> target) {
    // 获取 Feign 接口及注解信息的方法,随后封装为 Map<接口方法签名字符串,接口方法处理类> 形式返回。
    // MethodHandler实例为子类 SynchronousMethodHandler 。
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    // 把 Map<String, MethodHandler> 转化为 Map<Method, MethodHandler> , 方便构建代理类。
    for (Method method : target.type().getMethods()) {
      // ...
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
    // 根据 Map<Method, MethodHandler> 创建代理类。
    // 我们看下面 FeignInvocationHandler, 实际上就是根据当前方法,取出对应的增强逻辑进行执行。
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 生成代理类
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
   // ...
    return proxy;
  }

  static class FeignInvocationHandler implements InvocationHandler {
    private final Target target;
    private final Map<Method, MethodHandler> dispatch;
    // ...
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // ...
      return dispatch.get(method).invoke(args);
    }
  }
}

  至此,FeignClient 的代理类就生成结束了,回头我们在类中注入 FeignClient,获取到的就是 Feign 的代理了。

  那么接下来,我们的目标就放在 MethodHandler 上,去查看它是如何做增强的。

SynchronousMethodHandler

  第一步,我们来看代理方法中拥有的属性。基本上,就是我们在《『 OpenFeign』使用与配置篇》中提到的那些配置项。

final class SynchronousMethodHandler implements MethodHandler {
  // 接口方法的基本信息
  private final MethodMetadata metadata;
  // 可以认为是 FeignClient 的 Class 对象持有者
  private final Target<?> target;
  // HTTP 请求执行器
  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 ExceptionPropagationPolicy propagationPolicy;

}

  第二步,我们来查看执行方法。

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 {
        // 从这里我们可以知道,只有 RetryableException 可以被重试。
        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;
          }
        }
        // ...
        continue;
      }
    }
  }

  // 执行请求,并且将响应解码
  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    // 在这里获取 Request,并且内部执行了请求拦截器
    Request request = targetRequest(template);

    Response response;
    long start = System.nanoTime();
    try {
      // 通过 Client 执行请求,还记上面看到 Client 实例是子类 LoadBalancerFeignClient。
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      // ...
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    if (decoder != null)
      return decoder.decode(response, metadata.returnType());
    // ...
    try {
      // ....
      return resultFuture.join();
    } catch (CompletionException e) {
      // 。。。
    }
  }

}

  至此,Feign 发送请求前后的处理逻辑,我们基本已经探究结束了。

  接下来就是,Client 是如何发送 HTTP 请求的,以及负载均衡是如何处理的。

  

执行请求与负载均衡

  负载均衡这里涉及到 Ribbon , 不是我们这篇文章的重心,所以下面会简要地摘取核心代码来理解。

  LoadBalancerFeignClient

// FeignClient HTTP 执行器
public class LoadBalancerFeignClient implements Client {
   // ...
   @Override
   public Response execute(Request request, Request.Options options) throws IOException {
      try {
         URI asUri = URI.create(request.url());
         String clientName = asUri.getHost();
         URI uriWithoutHost = cleanUrl(request.url(), clientName);
         FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
               this.delegate, request, uriWithoutHost);

         IClientConfig requestConfig = getClientConfig(options, clientName);
         // 这里创建 一个 FeignLoadBalancer 负载均衡器,然后执行 executeWithLoadBalancer 方法。
         return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
      }
      catch (ClientException e) {
         // ...
      }
   }
}

  FeignLoadBalancer

public class FeignLoadBalancer extends
      AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
  
}


public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> 
         extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {
    // 当调用者想要将请求分派到负载均衡器选择的服务器时,应该使用此方法,而不是在请求的 URI 中指定服务器。
    // 它通过调用 reconstructURIWithServer(Server, URI) 计算最终的URI,
    // 然后调用 executeWithLoadBalancer(ClientRequest, IClientConfig) 。
    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

        try {
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
           // 。。。
        }
    }
}


  LoadBalancerCommand

public class LoadBalancerCommand<T> {
   // ...
    public Observable<T> submit(final ServerOperation<T> operation) {
   // ...
        // Use the load balancer
   // 这里的关键之处在于 selectServer() , 即选择一个客户端。
        Observable<T> o = (server == null ? selectServer() : Observable.just(server))
                .concatMap(new Func1<Server, Observable<T>>() {
                    @Override
                    // Called for each server being selected
                    public Observable<T> call(Server server) {
         // ....
                        // Called for each attempt and retry
                        Observable<T> o = Observable
                                .just(server)
                                .concatMap(new Func1<Server, Observable<T>>() {
                                    @Override
                                    public Observable<T> call(final Server server) {
                                        return operation.call(server).doOnEach(new Observer<T>() {
                                            // ....
                                        });
                                    }
                                });
                    
                        if (maxRetrysSame > 0) 
                            o = o.retry(retryPolicy(maxRetrysSame, true));
                        return o;
                    }
                });
        // ...
    }

    private Observable<Server> selectServer() {
        return Observable.create(new OnSubscribe<Server>() {
            @Override
            public void call(Subscriber<? super Server> next) {
                try {
               // 调用 loadBalancerContext.getServerFromLoadBalancer 获取 Server 信息。
                    Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);   
                    next.onNext(server);
                    next.onCompleted();
                } catch (Exception e) {
                    next.onError(e);
                }
            }
        });
    }
}

  LoadBalancerContext

public class LoadBalancerContext implements IClientConfigAware {
   // 只取两行核心代码
   public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
      // 
      ILoadBalancer lb = getLoadBalancer();
      Server svc = lb.chooseServer(loadBalancerKey);
   }
}

  BaseLoadBalancer

    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
      // 最终,通过 IRule#choose 获取选择了一个 Server。
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

  这里逻辑嵌套地比较深,不要绕晕了哦。LoadBalancerCommand 里 submit 方法体中,获取到 Server 之后,执行了 operation.call(server),即下面这段逻辑:

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

        try {
            return command.submit(
                new ServerOperation<T>() {
               // 这段逻辑
                    @Override
                    public Observable<T> call(Server server) {
         // 这里就是用 Server 服务端的真实 ip port 去替换服务名。
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
         // 填充为真实的 url 地址
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
             // 执行请求
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
           // 。。。
        }
    }

   @Override
   public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException {
      Request.Options options;
      // 该配置上的超时时间还得给配置上
      if (configOverride != null) {
         RibbonProperties override = RibbonProperties.from(configOverride);
         options = new Request.Options(override.connectTimeout(connectTimeout),
               TimeUnit.MILLISECONDS, override.readTimeout(readTimeout),
               TimeUnit.MILLISECONDS, override.isFollowRedirects(followRedirects));
      }else {
         options = new Request.Options(connectTimeout, TimeUnit.MILLISECONDS,
            readTimeout, TimeUnit.MILLISECONDS, followRedirects);
      }
      // 执行请求 
      Response response = request.client().execute(request.toRequest(), options);
      return new RibbonResponse(request.getUri(), response);
   }

  至于 IRule 接口,就是由各使用方自行提供了,例如 Nacos 就是自行实现了

public class NacosRule extends AbstractLoadBalancerRule {
   @Autowired
   private NacosDiscoveryProperties nacosDiscoveryProperties;
   @Autowired
   private NacosServiceManager nacosServiceManager;

   @Override
   public Server choose(Object key) {
      try {
         String clusterName = this.nacosDiscoveryProperties.getClusterName();
         String group = this.nacosDiscoveryProperties.getGroup();
         DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
         String name = loadBalancer.getName();

         NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());
         // 获取 服务端 实例
         List<Instance> instances = namingService.selectInstances(name, group, true);
         // ...
         List<Instance> instancesToChoose = instances;
         if (StringUtils.isNotBlank(clusterName)) {
            List<Instance> sameClusterInstances = instances.stream()
                  .filter(instance -> Objects.equals(clusterName,instance.getClusterName()))
                  .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(sameClusterInstances)) {
               instancesToChoose = sameClusterInstances;
            } // ...
         }
         Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
         return new NacosServer(instance);
      }
      catch (Exception e) {
         // ...
      }
   }
}

  执行请求

  如果我们没有配置其他的HTTP底层,就是使用的默认的 HttpURLConnection 去发送请求。

// Client.Default
@Override
public Response execute(Request request, Options options) throws IOException {
  HttpURLConnection connection = convertAndSend(request, options);
  return convertResponse(connection, request);
}

  但是 HttpURLConnection 的性能不是很好,所以我们平时也会将其替换为其他的 HTTP Client。

  例如,通过配置 okhttp 更换为 okhttp 发起请求:

  pom

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>xx.xx</version>
        </dependency>

  yml 与 配置

feign:
  okhttp:
    enabled: true

  OkHttpClient

public final class OkHttpClient implements Client {
  // ...
  @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()
        || delegate.followRedirects() != options.isFollowRedirects()) {
      requestScoped = delegate.newBuilder()
          .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
          .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
          .followRedirects(options.isFollowRedirects())
          .build();
    } else {
      requestScoped = delegate;
    }
    Request request = toOkHttpRequest(input);
    Response response = requestScoped.newCall(request).execute();
    return toFeignResponse(response, input).toBuilder().request(input).build();
  }
  
}

小结

  OpenFeign 的原理分析篇到这里基本结束了。

  在这篇里,主要分析了 Spring Boot 项目里,OpenFeign 是如何启动的、以及 Feign 中是如何使用代理做增强。

  最后简单地分析了下,执行请求以及负载均衡的流程。